84 lines
2.4 KiB
Go
84 lines
2.4 KiB
Go
// Package handler HTTP 处理层。
|
||
//
|
||
// 职责边界:只做三件事 —— 解析请求参数、调用 service、写出响应。
|
||
// 不包含任何 SQL 与业务规则。
|
||
package handler
|
||
|
||
import (
|
||
"errors"
|
||
"net/http"
|
||
"strconv"
|
||
"strings"
|
||
|
||
"fashionapi/internal/pkg/response"
|
||
"fashionapi/internal/service"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
)
|
||
|
||
// fail 把 service 层错误翻译为 HTTP 响应。
|
||
//
|
||
// 业务错误携带自己的状态码与文案,原样透传(保证与原项目逐字一致);
|
||
// 其余未预期错误统一按 500 处理,避免内部细节泄漏成不可控的响应。
|
||
func fail(c *gin.Context, err error) {
|
||
var svcErr *service.Error
|
||
if errors.As(err, &svcErr) {
|
||
response.Error(c, svcErr.Status, svcErr.Message)
|
||
return
|
||
}
|
||
response.Error(c, http.StatusInternalServerError, err.Error())
|
||
}
|
||
|
||
// queryInt 读取整型查询参数,缺失或非法时返回默认值。
|
||
func queryInt(c *gin.Context, key string, def int) int {
|
||
v := strings.TrimSpace(c.Query(key))
|
||
if v == "" {
|
||
return def
|
||
}
|
||
n, err := strconv.Atoi(v)
|
||
if err != nil {
|
||
return def
|
||
}
|
||
return n
|
||
}
|
||
|
||
// queryBool 读取布尔查询参数。
|
||
//
|
||
// def 为该参数缺失时的取值:
|
||
// - def=true(如某开关默认开启):只有显式传 0 / false 才关闭;
|
||
// - def=false(如 featured):只有显式传 1 / true 才开启。
|
||
func queryBool(c *gin.Context, key string, def bool) bool {
|
||
v := strings.ToLower(strings.TrimSpace(c.Query(key)))
|
||
if v == "" {
|
||
return def
|
||
}
|
||
if def {
|
||
return v != "0" && v != "false"
|
||
}
|
||
return v == "1" || v == "true"
|
||
}
|
||
|
||
// parseLocale 解析请求语言,优先级:X-Locale 头 > ?locale=cn|en > Accept-Language > 默认 en。
|
||
// 与前端 i18n 的 LOCALES=['en','cn']、DEFAULT_LOCALE='en' 保持一致。
|
||
// X-Locale 由前端统一经请求头透传「当前页面语言」,显式且不受浏览器语言设置干扰。
|
||
func parseLocale(c *gin.Context) string {
|
||
if l := strings.ToLower(strings.TrimSpace(c.GetHeader("X-Locale"))); l == "cn" || l == "en" {
|
||
return l
|
||
}
|
||
if l := strings.ToLower(strings.TrimSpace(c.Query("locale"))); l == "cn" || l == "en" {
|
||
return l
|
||
}
|
||
if al := c.GetHeader("Accept-Language"); al != "" {
|
||
for _, part := range strings.Split(al, ",") {
|
||
code := strings.ToLower(strings.TrimSpace(strings.SplitN(part, ";", 2)[0]))
|
||
switch {
|
||
case code == "zh" || strings.HasPrefix(code, "zh-"):
|
||
return "cn"
|
||
case strings.HasPrefix(code, "en"):
|
||
return "en"
|
||
}
|
||
}
|
||
}
|
||
return "en"
|
||
}
|