1e3dc11d59
feat(test-suite): 新增测试套件模块,包含API测试客户端和测试配置 fix(api): 修复数据库实体和仓库的删除操作返回值 style(api): 统一数据库表名和字段命名 perf(api): 添加缓存注解提升配置查询性能 test(api): 添加H2测试数据库配置支持 chore: 清理旧的测试文件和脚本
174 lines
3.9 KiB
Python
174 lines
3.9 KiB
Python
"""
|
|
字符串处理工具类
|
|
|
|
提供字符串相关的工具方法
|
|
|
|
作者: 张翔
|
|
日期: 2026-04-01
|
|
"""
|
|
|
|
import random
|
|
import string
|
|
import re
|
|
from typing import Optional
|
|
|
|
|
|
class StringHelper:
|
|
"""字符串处理工具类"""
|
|
|
|
@staticmethod
|
|
def truncate(text: str, max_length: int = 100, suffix: str = "...") -> str:
|
|
"""
|
|
截断字符串
|
|
|
|
Args:
|
|
text: 原始字符串
|
|
max_length: 最大长度
|
|
suffix: 后缀
|
|
|
|
Returns:
|
|
截断后的字符串
|
|
"""
|
|
if not text:
|
|
return ""
|
|
|
|
if len(text) <= max_length:
|
|
return text
|
|
|
|
return text[:max_length] + suffix
|
|
|
|
@staticmethod
|
|
def mask_phone(phone: str) -> str:
|
|
"""
|
|
手机号脱敏
|
|
|
|
Args:
|
|
phone: 手机号
|
|
|
|
Returns:
|
|
脱敏后的手机号
|
|
"""
|
|
if not phone or len(phone) < 7:
|
|
return phone
|
|
|
|
return phone[:3] + "****" + phone[-4:]
|
|
|
|
@staticmethod
|
|
def mask_email(email: str) -> str:
|
|
"""
|
|
邮箱脱敏
|
|
|
|
Args:
|
|
email: 邮箱
|
|
|
|
Returns:
|
|
脱敏后的邮箱
|
|
"""
|
|
if not email or "@" not in email:
|
|
return email
|
|
|
|
parts = email.split("@")
|
|
username = parts[0]
|
|
domain = parts[1]
|
|
|
|
if len(username) <= 2:
|
|
masked_username = username[0] + "***"
|
|
else:
|
|
masked_username = username[:2] + "***"
|
|
|
|
return f"{masked_username}@{domain}"
|
|
|
|
@staticmethod
|
|
def mask_id_card(id_card: str) -> str:
|
|
"""
|
|
身份证号脱敏
|
|
|
|
Args:
|
|
id_card: 身份证号
|
|
|
|
Returns:
|
|
脱敏后的身份证号
|
|
"""
|
|
if not id_card or len(id_card) < 8:
|
|
return id_card
|
|
|
|
return id_card[:4] + "**********" + id_card[-4:]
|
|
|
|
@staticmethod
|
|
def random_string(length: int = 16, chars: str = None) -> str:
|
|
"""
|
|
生成随机字符串
|
|
|
|
Args:
|
|
length: 长度
|
|
chars: 字符集,默认为字母数字
|
|
|
|
Returns:
|
|
随机字符串
|
|
"""
|
|
if chars is None:
|
|
chars = string.ascii_letters + string.digits
|
|
|
|
return ''.join(random.choice(chars) for _ in range(length))
|
|
|
|
@staticmethod
|
|
def camel_to_snake(name: str) -> str:
|
|
"""
|
|
驼峰命名转下划线命名
|
|
|
|
Args:
|
|
name: 驼峰命名字符串
|
|
|
|
Returns:
|
|
下划线命名字符串
|
|
"""
|
|
if not name:
|
|
return ""
|
|
|
|
s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name)
|
|
return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower()
|
|
|
|
@staticmethod
|
|
def snake_to_camel(name: str) -> str:
|
|
"""
|
|
下划线命名转驼峰命名
|
|
|
|
Args:
|
|
name: 下划线命名字符串
|
|
|
|
Returns:
|
|
驼峰命名字符串
|
|
"""
|
|
if not name:
|
|
return ""
|
|
|
|
components = name.split('_')
|
|
return components[0] + ''.join(x.title() for x in components[1:])
|
|
|
|
@staticmethod
|
|
def is_empty(text: Optional[str]) -> bool:
|
|
"""
|
|
判断字符串是否为空
|
|
|
|
Args:
|
|
text: 字符串
|
|
|
|
Returns:
|
|
是否为空
|
|
"""
|
|
return text is None or text.strip() == ""
|
|
|
|
@staticmethod
|
|
def default_if_empty(text: Optional[str], default: str) -> str:
|
|
"""
|
|
如果字符串为空则返回默认值
|
|
|
|
Args:
|
|
text: 字符串
|
|
default: 默认值
|
|
|
|
Returns:
|
|
字符串或默认值
|
|
"""
|
|
return text if not StringHelper.is_empty(text) else default
|