208 lines
6.5 KiB
Python
208 lines
6.5 KiB
Python
"""
|
||
用户ID生成工具
|
||
提供用户名验证和user_id自动生成功能
|
||
"""
|
||
|
||
import re
|
||
|
||
try:
|
||
from pypinyin import lazy_pinyin, Style
|
||
PYPINYIN_AVAILABLE = True
|
||
except ImportError:
|
||
PYPINYIN_AVAILABLE = False
|
||
|
||
|
||
def to_pinyin(text: str) -> str:
|
||
"""
|
||
将中文转换为拼音
|
||
优先使用pypinyin库,如果不可用则使用简化版本
|
||
"""
|
||
if PYPINYIN_AVAILABLE:
|
||
# 使用pypinyin进行转换
|
||
pinyin_list = lazy_pinyin(text, style=Style.NORMAL)
|
||
return ''.join(pinyin_list)
|
||
else:
|
||
# 简化的拼音映射表(备用方案)
|
||
pinyin_map = {
|
||
'张': 'zhang', '王': 'wang', '李': 'li', '刘': 'liu', '陈': 'chen',
|
||
'杨': 'yang', '赵': 'zhao', '黄': 'huang', '周': 'zhou', '吴': 'wu',
|
||
'徐': 'xu', '孙': 'sun', '胡': 'hu', '朱': 'zhu', '高': 'gao',
|
||
'林': 'lin', '何': 'he', '郭': 'guo', '马': 'ma', '罗': 'luo',
|
||
'梁': 'liang', '宋': 'song', '郑': 'zheng', '谢': 'xie', '韩': 'han',
|
||
'唐': 'tang', '冯': 'feng', '于': 'yu', '董': 'dong', '萧': 'xiao',
|
||
'程': 'cheng', '曹': 'cao', '袁': 'yuan', '邓': 'deng', '许': 'xu',
|
||
'傅': 'fu', '沈': 'shen', '曾': 'zeng', '彭': 'peng', '吕': 'lv',
|
||
'苏': 'su', '卢': 'lu', '蒋': 'jiang', '蔡': 'cai', '贾': 'jia',
|
||
'丁': 'ding', '魏': 'wei', '薛': 'xue', '叶': 'ye', '阎': 'yan',
|
||
'余': 'yu', '潘': 'pan', '杜': 'du', '戴': 'dai', '夏': 'xia',
|
||
'钟': 'zhong', '汪': 'wang', '田': 'tian', '任': 'ren', '姜': 'jiang',
|
||
'范': 'fan', '方': 'fang', '石': 'shi', '姚': 'yao', '谭': 'tan',
|
||
'廖': 'liao', '邹': 'zou', '熊': 'xiong', '金': 'jin', '陆': 'lu',
|
||
'郝': 'hao', '孔': 'kong', '白': 'bai', '崔': 'cui', '康': 'kang',
|
||
'毛': 'mao', '邱': 'qiu', '秦': 'qin', '江': 'jiang', '史': 'shi',
|
||
'顾': 'gu', '侯': 'hou', '邵': 'shao', '孟': 'meng', '龙': 'long',
|
||
'万': 'wan', '段': 'duan', '漕': 'cao', '钱': 'qian', '汤': 'tang',
|
||
# 添加一些常用字
|
||
'小': 'xiao', '大': 'da', '明': 'ming', '强': 'qiang', '文': 'wen',
|
||
'华': 'hua', '建': 'jian', '国': 'guo', '军': 'jun', '伟': 'wei',
|
||
'超': 'chao', '雷': 'lei', '鹏': 'peng', '俊': 'jun', '敏': 'min',
|
||
'志': 'zhi', '勇': 'yong', '杰': 'jie', '涛': 'tao', '斌': 'bin',
|
||
'辉': 'hui', '阳': 'yang', '磊': 'lei', '刚': 'gang',
|
||
'飞': 'fei', '翔': 'xiang', '波': 'bo', '峰': 'feng', '凯': 'kai',
|
||
'静': 'jing', '丽': 'li', '秀': 'xiu', '红': 'hong', '霞': 'xia',
|
||
'洁': 'jie', '燕': 'yan', '艳': 'yan', '娟': 'juan', '玲': 'ling',
|
||
'琳': 'lin', '萍': 'ping', '芳': 'fang', '莉': 'li', '婷': 'ting',
|
||
'慧': 'hui', '娜': 'na', '丹': 'dan', '青': 'qing', '倩': 'qian',
|
||
}
|
||
|
||
result = []
|
||
for char in text:
|
||
if char in pinyin_map:
|
||
result.append(pinyin_map[char])
|
||
elif char.isalpha():
|
||
result.append(char.lower())
|
||
elif char.isdigit():
|
||
result.append(char)
|
||
# 忽略其他字符
|
||
|
||
return ''.join(result)
|
||
|
||
|
||
def validate_username(username: str) -> tuple[bool, str]:
|
||
"""
|
||
验证用户名格式
|
||
|
||
Args:
|
||
username: 用户名
|
||
|
||
Returns:
|
||
Tuple[bool, str]: (是否有效, 错误信息)
|
||
"""
|
||
if not username:
|
||
return False, "用户名不能为空"
|
||
|
||
if len(username) < 2:
|
||
return False, "用户名长度不能少于2个字符"
|
||
|
||
if len(username) > 20:
|
||
return False, "用户名长度不能超过20个字符"
|
||
|
||
# 检查是否包含不允许的字符
|
||
# 允许中文、英文、数字、下划线
|
||
if not re.match(r'^[\u4e00-\u9fa5a-zA-Z0-9_]+$', username):
|
||
return False, "用户名只能包含中文、英文、数字和下划线"
|
||
|
||
return True, ""
|
||
|
||
|
||
def generate_user_id(username: str) -> str:
|
||
"""
|
||
根据用户名生成user_id
|
||
|
||
Args:
|
||
username: 用户名
|
||
|
||
Returns:
|
||
str: 生成的user_id
|
||
"""
|
||
# 1. 基本清理
|
||
username = username.strip()
|
||
|
||
# 2. 转换为拼音(如果包含中文)
|
||
user_id = to_pinyin(username)
|
||
|
||
# 3. 处理特殊字符,只保留字母、数字和下划线
|
||
user_id = re.sub(r'[^a-zA-Z0-9_]', '', user_id)
|
||
|
||
# 4. 确保不以数字开头
|
||
if user_id and user_id[0].isdigit():
|
||
user_id = 'u' + user_id
|
||
|
||
# 5. 如果为空或太短,使用默认前缀
|
||
if len(user_id) < 2:
|
||
user_id = 'user' + str(hash(username) % 10000).zfill(4)
|
||
|
||
# 6. 长度限制
|
||
if len(user_id) > 20:
|
||
user_id = user_id[:20]
|
||
|
||
return user_id.lower()
|
||
|
||
|
||
def generate_unique_user_id(username: str, existing_user_ids: list[str]) -> str:
|
||
"""
|
||
生成唯一的user_id,如果重复则添加数字后缀
|
||
|
||
Args:
|
||
username: 用户名
|
||
existing_user_ids: 已存在的user_id列表
|
||
|
||
Returns:
|
||
str: 唯一的user_id
|
||
"""
|
||
base_user_id = generate_user_id(username)
|
||
|
||
# 如果不重复,直接返回
|
||
if base_user_id not in existing_user_ids:
|
||
return base_user_id
|
||
|
||
# 如果重复,添加数字后缀
|
||
counter = 1
|
||
while True:
|
||
candidate = f"{base_user_id}{counter}"
|
||
if candidate not in existing_user_ids:
|
||
return candidate
|
||
counter += 1
|
||
|
||
# 防止无限循环
|
||
if counter > 9999:
|
||
# 使用时间戳作为后缀
|
||
import time
|
||
candidate = f"{base_user_id}{int(time.time()) % 10000}"
|
||
return candidate
|
||
|
||
|
||
def is_valid_phone_number(phone: str) -> bool:
|
||
"""
|
||
验证手机号格式(支持中国大陆手机号)
|
||
|
||
Args:
|
||
phone: 手机号字符串
|
||
|
||
Returns:
|
||
bool: 是否为有效手机号
|
||
"""
|
||
if not phone:
|
||
return False
|
||
|
||
# 移除空格和特殊字符
|
||
phone = re.sub(r'[\s\-\(\)]', '', phone)
|
||
|
||
# 中国大陆手机号格式:1开头,第二位是3-9,总共11位
|
||
pattern = r'^1[3-9]\d{9}$'
|
||
|
||
return bool(re.match(pattern, phone))
|
||
|
||
|
||
def normalize_phone_number(phone: str) -> str:
|
||
"""
|
||
标准化手机号格式
|
||
|
||
Args:
|
||
phone: 原始手机号
|
||
|
||
Returns:
|
||
str: 标准化后的手机号
|
||
"""
|
||
if not phone:
|
||
return ""
|
||
|
||
# 移除所有非数字字符
|
||
phone = re.sub(r'\D', '', phone)
|
||
|
||
# 如果是中国大陆手机号,确保格式正确
|
||
if len(phone) == 11 and phone.startswith('1'):
|
||
return phone
|
||
|
||
return phone
|