Files

80 lines
2.7 KiB
Go
Raw Permalink 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 sitecode 统一解析请求中的分销站点编码。
//
// 此前各业务模块(auth/order/wallet/chat/notification/withdrawal/listing/distribution)
// 各自内联解析,优先级和 TrimSpace 处理不一致,且对旧头 X-Site-Code 的支持参差不齐。
// 本包收敛为两个入口:
//
// - FromRequest:常规业务接口使用。SPA 前端(frontend/src/shared/api/client.ts)
// 会为每个请求统一注入 X-Distribution-Site-Code 头,因此头是权威来源;
// query 参数与旧头仅作为非 SPA 调用(脚本、排障)的兜底。
// - FromQueryThenHeader:仅用于 GET /distribution/site-info 站点探测。
// SPA 初始化探测新站点时会话头尚未建立,此时显式 query 参数必须压过会话头。
package sitecode
import (
"net"
"strings"
"github.com/gin-gonic/gin"
)
// HeaderName 是前端统一注入的权威站点编码请求头。
const HeaderName = "X-Distribution-Site-Code"
// LegacyHeaderName 是历史版本使用的站点编码请求头,仅作兜底兼容。
const LegacyHeaderName = "X-Site-Code"
// FromRequest 按权威优先级解析站点编码:
// X-Distribution-Site-Code 头 → X-Site-Code 旧头 → distribution_site_code / site_code query。
// 所有来源均做 TrimSpace,返回空串表示主站。
func FromRequest(c *gin.Context) string {
return firstNonEmpty(
c.GetHeader(HeaderName),
c.GetHeader(LegacyHeaderName),
c.Query("distribution_site_code"),
c.Query("site_code"),
)
}
// FromQueryThenHeader 仅用于站点探测接口(GET /distribution/site-info):
// 显式 query 参数优先于会话头。SPA 探测新站点时会话头里还是上一个站点的编码,
// 必须让 query 压过它,否则从主站跳转到分站时会探测到错误站点。
func FromQueryThenHeader(c *gin.Context) string {
return firstNonEmpty(
c.Query("code"),
c.Query("site_code"),
c.Query("distribution_site_code"),
c.GetHeader(HeaderName),
c.GetHeader(LegacyHeaderName),
)
}
// ExtractHost 从 HTTP Host(可能带端口、IPv6 方括号)中提取纯主机名,小写化。
func ExtractHost(rawHost string) string {
rawHost = strings.TrimSpace(rawHost)
if rawHost == "" {
return ""
}
var host string
if h, _, err := net.SplitHostPort(rawHost); err == nil {
host = h
} else {
host = rawHost
}
return strings.ToLower(strings.Trim(host, "[]"))
}
// IsLocalHost 判断主机名是否属于本地开发地址(不作为站点域名解析)。
func IsLocalHost(host string) bool {
return host == "localhost" || host == "127.0.0.1" || host == "::1"
}
func firstNonEmpty(values ...string) string {
for _, v := range values {
if trimmed := strings.TrimSpace(v); trimmed != "" {
return trimmed
}
}
return ""
}