77 lines
2.2 KiB
Go
77 lines
2.2 KiB
Go
// Package appcode 集中处理多 App 租户键的归一化和 context 透传。
|
||
package appcode
|
||
|
||
import (
|
||
"context"
|
||
"crypto/rand"
|
||
"fmt"
|
||
"strings"
|
||
)
|
||
|
||
const (
|
||
// Default 是当前开发阶段的种子 App;旧请求未显式携带 app_code 时统一落到 lalu。
|
||
Default = "lalu"
|
||
)
|
||
|
||
type contextKey struct{}
|
||
|
||
// Normalize 把外部输入收敛成数据库使用的短 app_code。
|
||
func Normalize(value string) string {
|
||
value = strings.ToLower(strings.TrimSpace(value))
|
||
if value == "" {
|
||
// 开发阶段不保留历史兼容表结构,但测试和后台任务仍需要一个稳定默认租户。
|
||
return Default
|
||
}
|
||
|
||
return value
|
||
}
|
||
|
||
// UsesRegionalLeaderboards 集中声明需要按用户所属区域隔离活动榜的 App。
|
||
// Lalu/Fami 同时运营中东和欧美区域,榜单 key 必须包含 region_id;其他 App 保持既有全 App 榜,
|
||
// 避免一次区域修复暗改尚未确认产品口径的租户。
|
||
func UsesRegionalLeaderboards(value string) bool {
|
||
switch Normalize(value) {
|
||
case "lalu", "fami":
|
||
return true
|
||
default:
|
||
return false
|
||
}
|
||
}
|
||
|
||
// WithContext 把 app_code 写入 context,供 service/repository 在不扩散方法签名的情况下取用。
|
||
func WithContext(ctx context.Context, value string) context.Context {
|
||
return context.WithValue(ctx, contextKey{}, Normalize(value))
|
||
}
|
||
|
||
// FromContext 从 context 读取 app_code;缺失时返回当前开发默认 App。
|
||
func FromContext(ctx context.Context) string {
|
||
if ctx == nil {
|
||
return Default
|
||
}
|
||
value, _ := ctx.Value(contextKey{}).(string)
|
||
return Normalize(value)
|
||
}
|
||
|
||
// NewScopedID 生成对客户端暴露的 App 长 ID。
|
||
// 格式固定为 app_code + "_" + uuid,保证同一个 ID 可直接作为用户长 ID、房间 ID 和腾讯 IM/RTC 字符串标识。
|
||
func NewScopedID(value string) string {
|
||
app := Normalize(value)
|
||
var bytes [16]byte
|
||
if _, err := rand.Read(bytes[:]); err != nil {
|
||
// crypto/rand 失败极少见;这里退化为全 0 仍保留格式,后续数据库唯一键会兜底冲突。
|
||
return fmt.Sprintf("%s_00000000-0000-4000-8000-000000000000", app)
|
||
}
|
||
|
||
bytes[6] = (bytes[6] & 0x0f) | 0x40
|
||
bytes[8] = (bytes[8] & 0x3f) | 0x80
|
||
|
||
return fmt.Sprintf("%s_%08x-%04x-%04x-%04x-%012x",
|
||
app,
|
||
bytes[0:4],
|
||
bytes[4:6],
|
||
bytes[6:8],
|
||
bytes[8:10],
|
||
bytes[10:16],
|
||
)
|
||
}
|