forked from baron/baron-sso
- Added support for fixed UUIDs during bulk registration (Search-first + ExternalID mapping) - Implemented idempotency and visibility restoration for soft-deleted users - Enhanced bulk upload UI to show 'New/Updated/Unchanged' status and modified fields - Added logic to reclaim identifiers (login_id) from colliding records - Added frontend E2E and backend unit tests for UUID integrity and conflict handling - Fixed i18n, formatting, and mock tests to satisfy code-check - Applied 'go fix' for 'omitzero' tags and general Go standards
197 lines
3.7 KiB
Go
197 lines
3.7 KiB
Go
package domain
|
|
|
|
import (
|
|
"errors"
|
|
"strings"
|
|
"unicode"
|
|
)
|
|
|
|
var hanmacSurnameRomanization = map[rune]string{
|
|
'한': "han",
|
|
'김': "kim",
|
|
'이': "lee",
|
|
'박': "park",
|
|
'최': "choi",
|
|
'정': "jung",
|
|
'조': "cho",
|
|
'강': "kang",
|
|
'윤': "yoon",
|
|
'장': "jang",
|
|
'임': "lim",
|
|
'림': "lim",
|
|
'신': "shin",
|
|
'오': "oh",
|
|
'서': "seo",
|
|
'권': "kwon",
|
|
'황': "hwang",
|
|
'안': "ahn",
|
|
'송': "song",
|
|
'전': "jeon",
|
|
'홍': "hong",
|
|
'유': "yoo",
|
|
'고': "ko",
|
|
'문': "moon",
|
|
'양': "yang",
|
|
'손': "son",
|
|
'배': "bae",
|
|
'백': "baek",
|
|
'허': "heo",
|
|
'남': "nam",
|
|
'심': "sim",
|
|
'노': "noh",
|
|
'하': "ha",
|
|
'곽': "kwak",
|
|
'성': "sung",
|
|
'차': "cha",
|
|
'주': "joo",
|
|
'우': "woo",
|
|
'구': "koo",
|
|
'민': "min",
|
|
'류': "ryu",
|
|
'나': "na",
|
|
'진': "jin",
|
|
'지': "ji",
|
|
'엄': "um",
|
|
'채': "chae",
|
|
'원': "won",
|
|
'천': "cheon",
|
|
'방': "bang",
|
|
'공': "gong",
|
|
'현': "hyun",
|
|
'함': "ham",
|
|
'여': "yeo",
|
|
'추': "choo",
|
|
'도': "do",
|
|
'소': "so",
|
|
'석': "seok",
|
|
'선': "sun",
|
|
'설': "seol",
|
|
'마': "ma",
|
|
'길': "gil",
|
|
'연': "yeon",
|
|
'위': "wi",
|
|
'표': "pyo",
|
|
'명': "myung",
|
|
'기': "ki",
|
|
'반': "ban",
|
|
'라': "ra",
|
|
'왕': "wang",
|
|
'금': "geum",
|
|
'옥': "ok",
|
|
'육': "yook",
|
|
'인': "in",
|
|
'맹': "maeng",
|
|
'제': "je",
|
|
'모': "mo",
|
|
'탁': "tak",
|
|
'국': "guk",
|
|
'어': "eo",
|
|
'은': "eun",
|
|
'편': "pyeon",
|
|
'용': "yong",
|
|
}
|
|
|
|
var hanmacInitialRomanization = []string{
|
|
"g", "g", "n", "d", "d", "r", "m", "b", "b", "s",
|
|
"s", "y", "j", "j", "c", "k", "t", "p", "h",
|
|
}
|
|
|
|
func SplitEmailDomain(email string) (string, string, error) {
|
|
normalized := strings.ToLower(strings.TrimSpace(email))
|
|
before, after, ok := strings.Cut(normalized, "@")
|
|
if !ok {
|
|
return "", "", errors.New("email must contain @")
|
|
}
|
|
if strings.Count(normalized, "@") != 1 {
|
|
return "", "", errors.New("email must contain one @")
|
|
}
|
|
localPart := strings.TrimSpace(before)
|
|
domainPart := strings.TrimSpace(after)
|
|
if domainPart == "" || !strings.Contains(domainPart, ".") {
|
|
return "", "", errors.New("email domain is invalid")
|
|
}
|
|
return localPart, domainPart, nil
|
|
}
|
|
|
|
func ExtractNormalizedEmailLocalPart(email string) (string, error) {
|
|
localPart, _, err := SplitEmailDomain(email)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if localPart == "" {
|
|
return "", errors.New("email local-part is empty")
|
|
}
|
|
return localPart, nil
|
|
}
|
|
|
|
func BuildKoreanNameEmailBase(name string) (string, bool, error) {
|
|
runes := compactNameRunes(name)
|
|
if len(runes) < 2 {
|
|
return "", true, nil
|
|
}
|
|
|
|
surname, ok := hanmacSurnameRomanization[runes[0]]
|
|
if !ok {
|
|
return "", true, nil
|
|
}
|
|
|
|
var builder strings.Builder
|
|
for _, r := range runes[1:] {
|
|
initial, ok := romanizedHangulInitial(r)
|
|
if !ok {
|
|
return "", true, nil
|
|
}
|
|
builder.WriteString(initial)
|
|
}
|
|
builder.WriteString(surname)
|
|
return builder.String(), false, nil
|
|
}
|
|
|
|
func MatchesSuggestedNameRule(localPart string, base string) bool {
|
|
localPart = strings.ToLower(strings.TrimSpace(localPart))
|
|
base = strings.ToLower(strings.TrimSpace(base))
|
|
if localPart == "" || base == "" {
|
|
return false
|
|
}
|
|
if localPart == base {
|
|
return true
|
|
}
|
|
if !strings.HasPrefix(localPart, base) {
|
|
return false
|
|
}
|
|
suffix := localPart[len(base):]
|
|
if suffix == "" {
|
|
return false
|
|
}
|
|
for _, r := range suffix {
|
|
if r < '0' || r > '9' {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func compactNameRunes(name string) []rune {
|
|
var runes []rune
|
|
for _, r := range strings.TrimSpace(name) {
|
|
if unicode.IsSpace(r) {
|
|
continue
|
|
}
|
|
runes = append(runes, r)
|
|
}
|
|
return runes
|
|
}
|
|
|
|
func romanizedHangulInitial(r rune) (string, bool) {
|
|
const hangulBase = 0xAC00
|
|
const hangulEnd = 0xD7A3
|
|
if r < hangulBase || r > hangulEnd {
|
|
return "", false
|
|
}
|
|
index := int(r-hangulBase) / 588
|
|
if index < 0 || index >= len(hanmacInitialRomanization) {
|
|
return "", false
|
|
}
|
|
return hanmacInitialRomanization[index], true
|
|
}
|