Files
hfb_sys/backend/internal/sitecode/sitecode.go
T
yml2213 8dc21d2757 refactor(sitecode): 统一各模块分销站点编码解析为公共包
- 新增 internal/sitecode 包:FromRequest 头优先解析、FromQueryThenHeader
  探测接口专用、ExtractHost/IsLocalHost 提取 Host 域名
- 替换 auth/order/listing/chat/notification/withdrawal/wallet/distribution
  八模块 17 处各自内联的解析逻辑,统一优先级与 TrimSpace 行为
- 修复 notification 漏 TrimSpace、探测接口会话头压过显式 query、
  GetPublicSiteInfo 把 localhost Host 当站点域名查库等不一致
- 顺手清理 auth 中无引用的 extractHost 死代码
- gofmt 修复 withdrawal/presenter、chat/file/payment 测试四个历史遗留格式问题
2026-09-17 14:54:38 +08:00

80 lines
2.7 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 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 ""
}