Files
backend_v2/internal/handler/handler.go
toom1996 30c9f21da4 update
2026-08-26 10:45:21 +08:00

110 lines
3.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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(如 only_with_articles):只有显式传 0 / false 才关闭;
// - def=false(如 featured):只有显式传 1 / true 才开启。
//
// 这两种语义都来自原项目,必须保持,否则前端 A-Z 索引的数据量会突变。
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"
}
// parseUint32CSV 解析逗号分隔的正整数列表(如 brand_ids=1,2,3),忽略非法项。
func parseUint32CSV(s string) []uint32 {
if strings.TrimSpace(s) == "" {
return nil
}
var out []uint32
for _, part := range strings.Split(s, ",") {
if n, err := strconv.Atoi(strings.TrimSpace(part)); err == nil && n > 0 {
out = append(out, uint32(n))
}
}
return out
}
// parseIntSlice 把字符串数组解析为整型数组,忽略非法项。
func parseIntSlice(in []string) []int {
if len(in) == 0 {
return nil
}
var out []int
for _, v := range in {
if n, err := strconv.Atoi(strings.TrimSpace(v)); err == nil {
out = append(out, n)
}
}
return out
}
// parseLocale 解析请求语言,优先级:?locale=cn|en > Accept-Language > 默认 en。
// 与前端 i18n/config.ts 的 LOCALES=['en','cn']、DEFAULT_LOCALE='en' 保持一致。
func parseLocale(c *gin.Context) string {
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"
}