249 lines
7.7 KiB
Go
249 lines
7.7 KiB
Go
package user
|
||
|
||
import (
|
||
"regexp"
|
||
"strings"
|
||
)
|
||
|
||
const (
|
||
// GlobalRegionCode 是没有命中任何显式国家映射时使用的兜底区域。
|
||
GlobalRegionCode = "GLOBAL"
|
||
|
||
// RegionStatusActive 表示区域映射参与国家到区域的实时解析。
|
||
RegionStatusActive = "active"
|
||
// RegionStatusDisabled 表示区域停止解析并释放 active 国家归属。
|
||
RegionStatusDisabled = "disabled"
|
||
|
||
// RegionRebuildTaskStatusPending 表示历史用户区域快照等待后台重算。
|
||
RegionRebuildTaskStatusPending = "pending"
|
||
// RegionRebuildTaskStatusRunning 表示任务已被某个 user-service 节点 claim。
|
||
RegionRebuildTaskStatusRunning = "running"
|
||
// RegionRebuildTaskStatusCompleted 表示该 country 下历史用户已经按任务 revision 重算完成。
|
||
RegionRebuildTaskStatusCompleted = "completed"
|
||
// RegionRebuildTaskStatusSkipped 表示任务 revision 已过期,不能覆盖更新映射。
|
||
RegionRebuildTaskStatusSkipped = "skipped"
|
||
// RegionRebuildTaskStatusFailed 表示任务处理失败,等待人工或后续重试策略处理。
|
||
RegionRebuildTaskStatusFailed = "failed"
|
||
)
|
||
|
||
var (
|
||
countryCodePattern = regexp.MustCompile(`^[A-Z]{2,3}$`)
|
||
isoAlpha3Pattern = regexp.MustCompile(`^[A-Z]{3}$`)
|
||
isoNumericPattern = regexp.MustCompile(`^[0-9]{3}$`)
|
||
phoneCountryCodePattern = regexp.MustCompile(`^\+[1-9][0-9]{0,2}$`)
|
||
shortRegionCodePattern = regexp.MustCompile(`^[A-Za-z0-9_]{2,32}$`)
|
||
descriptiveRegionCodePattern = regexp.MustCompile(`^[A-Za-z0-9][A-Za-z0-9 _-]{1,63}$`)
|
||
retiredRegionCodes = map[string]struct{}{
|
||
"AUSTRALIA AND NEW ZEALAND": {},
|
||
"CARIBBEAN": {},
|
||
"MELANESIA": {},
|
||
"MICRONESIA": {},
|
||
"POLYNESIA": {},
|
||
"SOUTHERN AFRICA": {},
|
||
"UNSPECIFIED": {},
|
||
}
|
||
)
|
||
|
||
// Country 是国家主数据;enabled 是唯一可用性开关,外部只提交 CountryCode。
|
||
type Country struct {
|
||
AppCode string
|
||
CountryID int64
|
||
CountryName string
|
||
CountryCode string
|
||
ISOAlpha3 string
|
||
ISONumeric string
|
||
CountryDisplayName string
|
||
PhoneCountryCode string
|
||
Flag string
|
||
Enabled bool
|
||
SortOrder int32
|
||
CreatedByUserID int64
|
||
UpdatedByUserID int64
|
||
CreatedAtMs int64
|
||
UpdatedAtMs int64
|
||
}
|
||
|
||
// Region 是业务区域主数据;Countries 只放 active 国家码快照。
|
||
type Region struct {
|
||
AppCode string
|
||
RegionID int64
|
||
RegionCode string
|
||
Name string
|
||
Status string
|
||
Countries []string
|
||
SortOrder int32
|
||
CreatedByUserID int64
|
||
UpdatedByUserID int64
|
||
CreatedAtMs int64
|
||
UpdatedAtMs int64
|
||
}
|
||
|
||
// RegionCountryMapping 表示一个 active 或历史国家到区域归属记录。
|
||
type RegionCountryMapping struct {
|
||
AppCode string
|
||
RegionID int64
|
||
CountryCode string
|
||
Status string
|
||
CreatedAtMs int64
|
||
UpdatedAtMs int64
|
||
}
|
||
|
||
// RegionChangeLog 是管理端区域配置审计,不记录用户隐私资料。
|
||
type RegionChangeLog struct {
|
||
AppCode string
|
||
RegionID int64
|
||
Operation string
|
||
BeforeJSON string
|
||
AfterJSON string
|
||
OperatorUserID int64
|
||
RequestID string
|
||
CreatedAtMs int64
|
||
}
|
||
|
||
// UserRegionRebuildTask 表示按国家分页刷新 users.region_id 的后台任务。
|
||
type UserRegionRebuildTask struct {
|
||
AppCode string
|
||
TaskID int64
|
||
RegionID int64
|
||
CountryCode string
|
||
TargetRegionID int64
|
||
MappingRevision int64
|
||
Status string
|
||
CursorUserID int64
|
||
LockedBy string
|
||
LockedUntilMs int64
|
||
AttemptCount int32
|
||
ErrorMessage string
|
||
CreatedAtMs int64
|
||
UpdatedAtMs int64
|
||
}
|
||
|
||
// RegionRebuildProcessResult 描述单次 worker 处理结果,便于测试和日志输出。
|
||
type RegionRebuildProcessResult struct {
|
||
AppCode string
|
||
TaskID int64
|
||
CountryCode string
|
||
TargetRegionID int64
|
||
Status string
|
||
ProcessedUsers int
|
||
CursorUserID int64
|
||
NoTask bool
|
||
}
|
||
|
||
// CountryFilter 描述国家管理列表过滤条件;国家只支持 enabled 维度过滤。
|
||
type CountryFilter struct {
|
||
AppCode string
|
||
Enabled *bool
|
||
}
|
||
|
||
// RegionFilter 描述区域管理列表过滤条件。
|
||
type RegionFilter struct {
|
||
AppCode string
|
||
Status string
|
||
}
|
||
|
||
// UpdateCountryCommand 描述修改国家展示字段的事务输入,CountryCode 创建后不可变。
|
||
type UpdateCountryCommand struct {
|
||
AppCode string
|
||
CountryID int64
|
||
CountryName string
|
||
ISOAlpha3 string
|
||
ISONumeric string
|
||
CountryDisplayName string
|
||
PhoneCountryCode string
|
||
Flag string
|
||
SortOrder int32
|
||
OperatorUserID int64
|
||
RequestID string
|
||
NowMs int64
|
||
}
|
||
|
||
// UpdateRegionCommand 描述修改区域展示字段的事务输入,不修改 countries。
|
||
type UpdateRegionCommand struct {
|
||
AppCode string
|
||
RegionID int64
|
||
RegionCode string
|
||
Name string
|
||
SortOrder int32
|
||
OperatorUserID int64
|
||
RequestID string
|
||
NowMs int64
|
||
}
|
||
|
||
// ReplaceRegionCountriesCommand 描述原子替换区域 active 国家归属的事务输入。
|
||
type ReplaceRegionCountriesCommand struct {
|
||
AppCode string
|
||
RegionID int64
|
||
Countries []string
|
||
OperatorUserID int64
|
||
RequestID string
|
||
NowMs int64
|
||
}
|
||
|
||
// NormalizeCountryCode 统一客户端和管理端提交的国家码形态。
|
||
func NormalizeCountryCode(code string) string {
|
||
return strings.ToUpper(strings.TrimSpace(code))
|
||
}
|
||
|
||
// ValidCountryCode 校验本系统允许的两到三位大写英文国家码。
|
||
func ValidCountryCode(code string) bool {
|
||
return countryCodePattern.MatchString(code)
|
||
}
|
||
|
||
// NormalizeISOAlpha3 统一 ISO alpha-3 辅助展示字段形态;空值表示未配置。
|
||
func NormalizeISOAlpha3(code string) string {
|
||
return strings.ToUpper(strings.TrimSpace(code))
|
||
}
|
||
|
||
// ValidISOAlpha3 校验非空 ISO alpha-3 或明确 user-assigned 三字码。
|
||
func ValidISOAlpha3(code string) bool {
|
||
return code == "" || isoAlpha3Pattern.MatchString(code)
|
||
}
|
||
|
||
// NormalizeISONumeric 统一 ISO numeric/M49 字符串形态,不能转整数以免破坏前导零。
|
||
func NormalizeISONumeric(code string) string {
|
||
return strings.TrimSpace(code)
|
||
}
|
||
|
||
// ValidISONumeric 校验非空 numeric 必须是三位数字字符串。
|
||
func ValidISONumeric(code string) bool {
|
||
return code == "" || isoNumericPattern.MatchString(code)
|
||
}
|
||
|
||
// NormalizePhoneCountryCode 统一 E.164 国家呼叫码字段,空值表示无可用电话元数据。
|
||
func NormalizePhoneCountryCode(code string) string {
|
||
return strings.TrimSpace(code)
|
||
}
|
||
|
||
// ValidPhoneCountryCode 校验国家呼叫码,不做唯一性假设,因为多个国家可以共享 +1。
|
||
func ValidPhoneCountryCode(code string) bool {
|
||
return code == "" || phoneCountryCodePattern.MatchString(code)
|
||
}
|
||
|
||
// NormalizeRegionCode 统一区域业务编码形态。
|
||
func NormalizeRegionCode(code string) string {
|
||
code = strings.TrimSpace(code)
|
||
if shortRegionCodePattern.MatchString(code) {
|
||
// 手工业务短码继续统一大写;导入的 subregion 描述码保留原始大小写和空格。
|
||
return strings.ToUpper(code)
|
||
}
|
||
|
||
return strings.Join(strings.Fields(code), " ")
|
||
}
|
||
|
||
// ValidRegionCode 校验区域编码,避免自由文本进入唯一键和审计口径。
|
||
func ValidRegionCode(code string) bool {
|
||
return shortRegionCodePattern.MatchString(code) || descriptiveRegionCodePattern.MatchString(code)
|
||
}
|
||
|
||
// IsGlobalRegionCode 判断区域是否是兜底 GLOBAL 区域。
|
||
func IsGlobalRegionCode(code string) bool {
|
||
return NormalizeRegionCode(code) == GlobalRegionCode
|
||
}
|
||
|
||
// IsRetiredRegionCode 判断区域是否已从业务区域集合中移除。
|
||
func IsRetiredRegionCode(code string) bool {
|
||
_, ok := retiredRegionCodes[strings.ToUpper(NormalizeRegionCode(code))]
|
||
return ok
|
||
}
|