78 lines
2.0 KiB
Go
78 lines
2.0 KiB
Go
package userstore
|
||
|
||
import (
|
||
"fmt"
|
||
"regexp"
|
||
"strings"
|
||
"unicode"
|
||
)
|
||
|
||
var tenantSlugRe = regexp.MustCompile(`^[a-z][a-z0-9-]{1,31}$`)
|
||
|
||
// 路径段保留字:不可用作公司 slug(与后续 /{slug}/ 路由冲突)
|
||
var reservedTenantSlugs = map[string]struct{}{
|
||
"api": {}, "ai": {}, "gateway": {}, "platform": {}, "_platform": {},
|
||
"admin": {}, "assets": {}, "static": {}, "www": {}, "m": {}, "public": {},
|
||
"health": {}, "ops": {}, "console": {}, "login": {}, "register": {},
|
||
"favicon.ico": {}, "robots.txt": {},
|
||
}
|
||
|
||
// NormalizeTenantSlug 规范化公司路径 slug(全局唯一,用于 www.yuxinda.com/{slug}/)。
|
||
func NormalizeTenantSlug(raw string) (string, error) {
|
||
s := strings.TrimSpace(strings.ToLower(raw))
|
||
s = strings.ReplaceAll(s, "_", "-")
|
||
s = strings.ReplaceAll(s, " ", "-")
|
||
var b strings.Builder
|
||
prevDash := false
|
||
for _, r := range s {
|
||
if (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') {
|
||
b.WriteRune(r)
|
||
prevDash = false
|
||
continue
|
||
}
|
||
if r == '-' {
|
||
if b.Len() == 0 || prevDash {
|
||
continue
|
||
}
|
||
b.WriteByte('-')
|
||
prevDash = true
|
||
continue
|
||
}
|
||
// 跳过其它字符(含中文);中文名需用户另填 slug
|
||
}
|
||
s = strings.Trim(b.String(), "-")
|
||
if s == "" {
|
||
return "", fmt.Errorf("公司路径 slug 不能为空(请用英文/数字,如 aaa)")
|
||
}
|
||
if !tenantSlugRe.MatchString(s) {
|
||
return "", fmt.Errorf("slug 须为 2–32 位,小写字母开头,仅含 a-z / 0-9 / -")
|
||
}
|
||
if _, bad := reservedTenantSlugs[s]; bad {
|
||
return "", fmt.Errorf("slug %q 为系统保留字", s)
|
||
}
|
||
return s, nil
|
||
}
|
||
|
||
// SuggestTenantSlug 从公司名生成候选(中文名可能得不到可用 slug,需人工填写)。
|
||
func SuggestTenantSlug(name string) string {
|
||
name = strings.TrimSpace(name)
|
||
if name == "" {
|
||
return ""
|
||
}
|
||
// 纯 ASCII 名:直接规范化
|
||
ascii := true
|
||
for _, r := range name {
|
||
if r > unicode.MaxASCII {
|
||
ascii = false
|
||
break
|
||
}
|
||
}
|
||
if ascii {
|
||
s, err := NormalizeTenantSlug(name)
|
||
if err == nil {
|
||
return s
|
||
}
|
||
}
|
||
return ""
|
||
}
|