release: v1.0.3 bug fix release
v1.0.3 定位为 bug fix release,收口 v1.0.2 引入的破坏性清理并修复 4 个轻量 bug + 依赖复查 + 版本号治理 + 文档对齐。同时包含此前未提交 的 v1.0.2 全部工作。 v1.0.3 变更: Fixed: - generateJTI 忽略 rand.Read 错误(jwt)— 改为 (string, error) 并传播 - QueryBuilder.Page Count 受残留 Limit 截断(repository)— countDB 加 Limit(-1).Offset(-1) - OSS/本地存储文件名冲突(storage)— 新增 uniqueFilename 加 8 字节随机后缀 - 数据库重试策略对不可恢复错误无效(database)— 新增 isTransientDBError Dependencies: - go mod tidy 补全 postgres 方言传递依赖;安全补丁升级 x/crypto v0.49→v0.53、golang-jwt/jwt/v5 v5.2.1→v5.3.1、gorilla/websocket v1.5.1→v1.5.3 Removed: - Breaking: 清理 v1.0.2 兼容别名(InitMySQL* / driverName) - 死代码 database.DBResolver - 代码中 292 行"评分/理由"自夸注释(#26,23 个文件) Changed: - Breaking: 错误码体系重构 CodeSuccess 1→0、CodeFail 0→1,删除 CodeInvalidParams,加编译期防撞码 - database/mysql.go → manager.go;Logger 拆分三独立 core 修复 Tee 重复写入 - 版本号常量化:app.go 新增 const Version 作为唯一来源,CLI/脚手架模板引用之,不再散落字面量 Security: - CORS 中间件按 W3C 规范修复 Allow-Credentials / Vary: Origin / 非白名单不回显 Added: - console 包显式 level 控制(SetLevel/WithLevel/LevelSilent,atomic.Int32 并发安全) - 新增测试 jwt/repository/storage/database/router/middleware/app - 文档:新增 CHANGELOG.md、Version_v1.0.2_report.md;更新 README/GUIDE 顶部更新日志 - 文档对齐:删除对不存在的 config.DefaultManager() getter 的描述(保持 API 与文档一致) 详见 CHANGELOG.md 与 README.md 更新日志。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -5,16 +5,12 @@ import (
|
||||
)
|
||||
|
||||
// ToInt 字符串转 int,失败返回 0
|
||||
// 评分: ⭐⭐⭐⭐
|
||||
// 理由: 简化类型转换,避免每次处理错误
|
||||
func ToInt(s string) int {
|
||||
n, _ := strconv.Atoi(s)
|
||||
return n
|
||||
}
|
||||
|
||||
// ToIntDefault 字符串转 int,失败返回默认值
|
||||
// 评分: ⭐⭐⭐⭐⭐
|
||||
// 理由: 提供默认值,更灵活
|
||||
func ToIntDefault(s string, def int) int {
|
||||
n, err := strconv.Atoi(s)
|
||||
if err != nil {
|
||||
@@ -24,16 +20,12 @@ func ToIntDefault(s string, def int) int {
|
||||
}
|
||||
|
||||
// ToInt64 字符串转 int64,失败返回 0
|
||||
// 评分: ⭐⭐⭐⭐
|
||||
// 理由: 大整数转换常用
|
||||
func ToInt64(s string) int64 {
|
||||
n, _ := strconv.ParseInt(s, 10, 64)
|
||||
return n
|
||||
}
|
||||
|
||||
// ToInt64Default 字符串转 int64,失败返回默认值
|
||||
// 评分: ⭐⭐⭐⭐⭐
|
||||
// 理由: 提供默认值,更灵活
|
||||
func ToInt64Default(s string, def int64) int64 {
|
||||
n, err := strconv.ParseInt(s, 10, 64)
|
||||
if err != nil {
|
||||
@@ -43,16 +35,12 @@ func ToInt64Default(s string, def int64) int64 {
|
||||
}
|
||||
|
||||
// ToUint64 字符串转 uint64,失败返回 0
|
||||
// 评分: ⭐⭐⭐⭐
|
||||
// 理由: 无符号整数转换
|
||||
func ToUint64(s string) uint64 {
|
||||
n, _ := strconv.ParseUint(s, 10, 64)
|
||||
return n
|
||||
}
|
||||
|
||||
// ToUint64Default 字符串转 uint64,失败返回默认值
|
||||
// 评分: ⭐⭐⭐⭐⭐
|
||||
// 理由: 提供默认值
|
||||
func ToUint64Default(s string, def uint64) uint64 {
|
||||
n, err := strconv.ParseUint(s, 10, 64)
|
||||
if err != nil {
|
||||
@@ -62,16 +50,12 @@ func ToUint64Default(s string, def uint64) uint64 {
|
||||
}
|
||||
|
||||
// ToFloat64 字符串转 float64,失败返回 0
|
||||
// 评分: ⭐⭐⭐⭐
|
||||
// 理由: 浮点数转换常用
|
||||
func ToFloat64(s string) float64 {
|
||||
n, _ := strconv.ParseFloat(s, 64)
|
||||
return n
|
||||
}
|
||||
|
||||
// ToFloat64Default 字符串转 float64,失败返回默认值
|
||||
// 评分: ⭐⭐⭐⭐⭐
|
||||
// 理由: 提供默认值
|
||||
func ToFloat64Default(s string, def float64) float64 {
|
||||
n, err := strconv.ParseFloat(s, 64)
|
||||
if err != nil {
|
||||
@@ -81,22 +65,16 @@ func ToFloat64Default(s string, def float64) float64 {
|
||||
}
|
||||
|
||||
// ToString 整数转字符串
|
||||
// 评分: ⭐⭐⭐⭐
|
||||
// 理由: 简化 strconv.Itoa 调用
|
||||
func ToString(n int) string {
|
||||
return strconv.Itoa(n)
|
||||
}
|
||||
|
||||
// ToString64 int64 转字符串
|
||||
// 评分: ⭐⭐⭐⭐
|
||||
// 理由: 大整数转字符串
|
||||
func ToString64(n int64) string {
|
||||
return strconv.FormatInt(n, 10)
|
||||
}
|
||||
|
||||
// CalcPageCount 计算总页数
|
||||
// 评分: ⭐⭐⭐⭐⭐
|
||||
// 理由: 分页查询必备,避免除零错误
|
||||
func CalcPageCount(total, pageSize int64) int64 {
|
||||
if total <= 0 || pageSize <= 0 {
|
||||
return 0
|
||||
@@ -105,8 +83,6 @@ func CalcPageCount(total, pageSize int64) int64 {
|
||||
}
|
||||
|
||||
// CalcOffset 计算分页偏移量
|
||||
// 评分: ⭐⭐⭐⭐⭐
|
||||
// 理由: 分页查询必备,自动处理页码边界
|
||||
func CalcOffset(page, pageSize int) int {
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
|
||||
@@ -11,16 +11,12 @@ import (
|
||||
)
|
||||
|
||||
// MD5 计算字符串的 MD5 哈希值
|
||||
// 评分: ⭐⭐⭐⭐
|
||||
// 理由: 常用哈希函数,但 MD5 已不安全,仅用于非安全场景(如文件校验)
|
||||
// 注意: 不应用于密码存储
|
||||
func MD5(s string) string {
|
||||
return MD5Bytes([]byte(s))
|
||||
}
|
||||
|
||||
// MD5Bytes 计算字节数组的 MD5 哈希值
|
||||
// 评分: ⭐⭐⭐⭐
|
||||
// 理由: 文件哈希常用
|
||||
func MD5Bytes(data []byte) string {
|
||||
h := md5.New()
|
||||
h.Write(data)
|
||||
@@ -28,8 +24,6 @@ func MD5Bytes(data []byte) string {
|
||||
}
|
||||
|
||||
// SHA1 计算字符串的 SHA1 哈希值
|
||||
// 评分: ⭐⭐⭐
|
||||
// 理由: SHA1 也已不安全,仅用于兼容旧系统
|
||||
// 注意: 不应用于密码存储
|
||||
func SHA1(s string) string {
|
||||
h := sha1.New()
|
||||
@@ -38,8 +32,6 @@ func SHA1(s string) string {
|
||||
}
|
||||
|
||||
// SHA256 计算字符串的 SHA256 哈希值
|
||||
// 评分: ⭐⭐⭐⭐⭐
|
||||
// 理由: 安全哈希算法,适用于数据完整性校验
|
||||
func SHA256(s string) string {
|
||||
h := sha256.New()
|
||||
h.Write([]byte(s))
|
||||
@@ -47,8 +39,6 @@ func SHA256(s string) string {
|
||||
}
|
||||
|
||||
// SHA256Bytes 计算字节数组的 SHA256 哈希值
|
||||
// 评分: ⭐⭐⭐⭐⭐
|
||||
// 理由: 文件哈希常用
|
||||
func SHA256Bytes(data []byte) string {
|
||||
h := sha256.New()
|
||||
h.Write(data)
|
||||
@@ -56,8 +46,6 @@ func SHA256Bytes(data []byte) string {
|
||||
}
|
||||
|
||||
// HashFile 计算文件的哈希值
|
||||
// 评分: ⭐⭐⭐⭐⭐
|
||||
// 理由: 大文件哈希,支持分块读取避免内存溢出
|
||||
func HashFile(path string, newHash func() hash.Hash) (string, error) {
|
||||
data, err := ReadFile(path)
|
||||
if err != nil {
|
||||
@@ -69,36 +57,26 @@ func HashFile(path string, newHash func() hash.Hash) (string, error) {
|
||||
}
|
||||
|
||||
// Base64Encode Base64 编码
|
||||
// 评分: ⭐⭐⭐⭐⭐
|
||||
// 理由: 常用编码函数
|
||||
func Base64Encode(data []byte) string {
|
||||
return base64.StdEncoding.EncodeToString(data)
|
||||
}
|
||||
|
||||
// Base64Decode Base64 解码
|
||||
// 评分: ⭐⭐⭐⭐⭐
|
||||
// 理由: 常用解码函数
|
||||
func Base64Decode(s string) ([]byte, error) {
|
||||
return base64.StdEncoding.DecodeString(s)
|
||||
}
|
||||
|
||||
// Base64URLEncode URL 安全的 Base64 编码
|
||||
// 评分: ⭐⭐⭐⭐⭐
|
||||
// 理由: URL 传输常用,替换 +/ 为 -_
|
||||
func Base64URLEncode(data []byte) string {
|
||||
return base64.URLEncoding.EncodeToString(data)
|
||||
}
|
||||
|
||||
// Base64URLDecode URL 安全的 Base64 解码
|
||||
// 评分: ⭐⭐⭐⭐⭐
|
||||
// 理由: URL 传输常用
|
||||
func Base64URLDecode(s string) ([]byte, error) {
|
||||
return base64.URLEncoding.DecodeString(s)
|
||||
}
|
||||
|
||||
// Nl2br 将换行符替换为 <br> 标签
|
||||
// 评分: ⭐⭐⭐
|
||||
// 理由: 特定场景使用(富文本显示),但现代前端框架通常自行处理
|
||||
func Nl2br(s string, isXhtml bool) string {
|
||||
var br string
|
||||
if isXhtml {
|
||||
|
||||
@@ -6,85 +6,61 @@ import (
|
||||
)
|
||||
|
||||
// NowUnix 返回当前秒级时间戳
|
||||
// 评分: ⭐⭐⭐⭐
|
||||
// 理由: 简单封装,语义清晰,避免每次写 time.Now().Unix()
|
||||
func NowUnix() int64 {
|
||||
return time.Now().Unix()
|
||||
}
|
||||
|
||||
// NowTimestamp 返回当前毫秒时间戳
|
||||
// 评分: ⭐⭐⭐⭐⭐
|
||||
// 理由: API接口常用毫秒时间戳,封装后更易用
|
||||
func NowTimestamp() int64 {
|
||||
return time.Now().UnixMilli()
|
||||
}
|
||||
|
||||
// FromUnix 秒级时间戳转 time.Time
|
||||
// 评分: ⭐⭐⭐⭐
|
||||
// 理由: 时间戳转换常用,封装后语义清晰
|
||||
func FromUnix(unix int64) time.Time {
|
||||
return time.Unix(unix, 0)
|
||||
}
|
||||
|
||||
// FromTimestamp 毫秒时间戳转 time.Time
|
||||
// 评分: ⭐⭐⭐⭐
|
||||
// 理由: 毫秒时间戳转换,API交互常用
|
||||
func FromTimestamp(timestamp int64) time.Time {
|
||||
return time.UnixMilli(timestamp)
|
||||
}
|
||||
|
||||
// FormatTime 格式化时间
|
||||
// 评分: ⭐⭐⭐⭐
|
||||
// 理由: 常用时间格式化封装,支持自定义布局
|
||||
func FormatTime(t time.Time, layout string) string {
|
||||
return t.Format(layout)
|
||||
}
|
||||
|
||||
// ParseTime 解析时间字符串
|
||||
// 评分: ⭐⭐⭐⭐
|
||||
// 理由: 时间解析封装,统一错误处理
|
||||
func ParseTime(timeStr, layout string) (time.Time, error) {
|
||||
return time.Parse(layout, timeStr)
|
||||
}
|
||||
|
||||
// FormatDateTime 格式化为标准日期时间 "2006-01-02 15:04:05"
|
||||
// 评分: ⭐⭐⭐⭐⭐
|
||||
// 理由: 预设常用格式,避免记忆 Go 的时间布局
|
||||
func FormatDateTime(t time.Time) string {
|
||||
return t.Format("2006-01-02 15:04:05")
|
||||
}
|
||||
|
||||
// FormatDate 格式化为日期 "2006-01-02"
|
||||
// 评分: ⭐⭐⭐⭐⭐
|
||||
// 理由: 预设常用格式
|
||||
func FormatDate(t time.Time) string {
|
||||
return t.Format("2006-01-02")
|
||||
}
|
||||
|
||||
// FormatTimeOnly 格式化为时间 "15:04:05"
|
||||
// 评分: ⭐⭐⭐⭐⭐
|
||||
// 理由: 预设常用格式
|
||||
func FormatTimeOnly(t time.Time) string {
|
||||
return t.Format("15:04:05")
|
||||
}
|
||||
|
||||
// StartOfDay 返回指定时间当天的开始时间 (00:00:00)
|
||||
// 评分: ⭐⭐⭐⭐⭐
|
||||
// 理由: 统计报表、日期范围查询常用
|
||||
func StartOfDay(t time.Time) time.Time {
|
||||
return time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location())
|
||||
}
|
||||
|
||||
// EndOfDay 返回指定时间当天的结束时间 (23:59:59.999999999)
|
||||
// 评分: ⭐⭐⭐⭐⭐
|
||||
// 理由: 统计报表、日期范围查询常用
|
||||
func EndOfDay(t time.Time) time.Time {
|
||||
return time.Date(t.Year(), t.Month(), t.Day(), 23, 59, 59, 999999999, t.Location())
|
||||
}
|
||||
|
||||
// StartOfWeek 返回指定时间当周的开始时间(周一为第一天)
|
||||
// 评分: ⭐⭐⭐⭐
|
||||
// 理由: 周报表统计常用
|
||||
func StartOfWeek(t time.Time) time.Time {
|
||||
weekday := int(t.Weekday())
|
||||
if weekday == 0 {
|
||||
@@ -95,30 +71,22 @@ func StartOfWeek(t time.Time) time.Time {
|
||||
}
|
||||
|
||||
// StartOfMonth 返回指定时间当月的开始时间
|
||||
// 评分: ⭐⭐⭐⭐⭐
|
||||
// 理由: 月度统计常用
|
||||
func StartOfMonth(t time.Time) time.Time {
|
||||
return time.Date(t.Year(), t.Month(), 1, 0, 0, 0, 0, t.Location())
|
||||
}
|
||||
|
||||
// EndOfMonth 返回指定时间当月的结束时间
|
||||
// 评分: ⭐⭐⭐⭐⭐
|
||||
// 理由: 月度统计常用
|
||||
func EndOfMonth(t time.Time) time.Time {
|
||||
return time.Date(t.Year(), t.Month()+1, 0, 23, 59, 59, 999999999, t.Location())
|
||||
}
|
||||
|
||||
// GetDateInt 返回 yyyyMMdd 格式的日期整数
|
||||
// 评分: ⭐⭐⭐
|
||||
// 理由: 特定场景使用(如分区键),但返回 int 可能不够通用
|
||||
func GetDateInt(t time.Time) int {
|
||||
ret, _ := strconv.Atoi(t.Format("20060102"))
|
||||
return ret
|
||||
}
|
||||
|
||||
// ParseDateInt 将 yyyyMMdd 格式的整数转为时间
|
||||
// 评分: ⭐⭐⭐
|
||||
// 理由: GetDateInt 的反向操作
|
||||
func ParseDateInt(date int) time.Time {
|
||||
year := date / 10000
|
||||
month := (date % 10000) / 100
|
||||
|
||||
@@ -8,16 +8,12 @@ import (
|
||||
)
|
||||
|
||||
// FileExists 检查文件是否存在
|
||||
// 评分: ⭐⭐⭐⭐⭐
|
||||
// 理由: 常用检查,语义清晰
|
||||
func FileExists(path string) bool {
|
||||
_, err := os.Stat(path)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// DirExists 检查目录是否存在
|
||||
// 评分: ⭐⭐⭐⭐⭐
|
||||
// 理由: 区分文件和目录检查,更精确
|
||||
func DirExists(path string) bool {
|
||||
info, err := os.Stat(path)
|
||||
if err != nil {
|
||||
@@ -27,15 +23,11 @@ func DirExists(path string) bool {
|
||||
}
|
||||
|
||||
// EnsureDir 确保目录存在,不存在则创建
|
||||
// 评分: ⭐⭐⭐⭐⭐
|
||||
// 理由: 日志、上传等场景常用,避免每次判断
|
||||
func EnsureDir(path string) error {
|
||||
return os.MkdirAll(path, 0755)
|
||||
}
|
||||
|
||||
// ReadFile 读取文件内容
|
||||
// 评分: ⭐⭐⭐⭐
|
||||
// 理由: 简化 os.ReadFile,增加存在性检查
|
||||
func ReadFile(path string) ([]byte, error) {
|
||||
if !FileExists(path) {
|
||||
return nil, fmt.Errorf("file not found: %s", path)
|
||||
@@ -44,8 +36,6 @@ func ReadFile(path string) ([]byte, error) {
|
||||
}
|
||||
|
||||
// WriteFile 写入文件内容(覆盖)
|
||||
// 评分: ⭐⭐⭐⭐
|
||||
// 理由: 简化文件写入,自动创建目录
|
||||
func WriteFile(path string, data []byte) error {
|
||||
dir := filepath.Dir(path)
|
||||
if err := EnsureDir(dir); err != nil {
|
||||
@@ -55,8 +45,6 @@ func WriteFile(path string, data []byte) error {
|
||||
}
|
||||
|
||||
// AppendFile 追加内容到文件
|
||||
// 评分: ⭐⭐⭐⭐
|
||||
// 理由: 日志追加场景常用
|
||||
func AppendFile(path string, data []byte) error {
|
||||
dir := filepath.Dir(path)
|
||||
if err := EnsureDir(dir); err != nil {
|
||||
@@ -72,8 +60,6 @@ func AppendFile(path string, data []byte) error {
|
||||
}
|
||||
|
||||
// CopyFile 复制文件
|
||||
// 评分: ⭐⭐⭐⭐⭐
|
||||
// 理由: 文件操作常用,使用 io.Copy 高效复制
|
||||
func CopyFile(dst, src string) error {
|
||||
srcFile, err := os.Open(src)
|
||||
if err != nil {
|
||||
@@ -98,8 +84,6 @@ func CopyFile(dst, src string) error {
|
||||
}
|
||||
|
||||
// FileSize 获取文件大小
|
||||
// 评分: ⭐⭐⭐⭐
|
||||
// 理由: 上传文件大小检查常用
|
||||
func FileSize(path string) (int64, error) {
|
||||
info, err := os.Stat(path)
|
||||
if err != nil {
|
||||
@@ -109,8 +93,6 @@ func FileSize(path string) (int64, error) {
|
||||
}
|
||||
|
||||
// RemoveFile 删除文件(忽略不存在的错误)
|
||||
// 评分: ⭐⭐⭐⭐
|
||||
// 理由: 清理临时文件时常用,避免额外判断
|
||||
func RemoveFile(path string) error {
|
||||
err := os.Remove(path)
|
||||
if os.IsNotExist(err) {
|
||||
|
||||
@@ -17,8 +17,6 @@ import (
|
||||
)
|
||||
|
||||
// HTTPClient HTTP 客户端封装
|
||||
// 评分: ⭐⭐⭐⭐⭐
|
||||
// 理由: 链式调用设计优雅,Transport 复用,连接池优化
|
||||
type HTTPClient struct {
|
||||
client *http.Client
|
||||
transport *http.Transport
|
||||
@@ -56,8 +54,6 @@ var DefaultHTTPClientConfig = HTTPClientConfig{
|
||||
}
|
||||
|
||||
// NewHTTPClient 创建 HTTP 客户端
|
||||
// 评分: ⭐⭐⭐⭐⭐
|
||||
// 理由: Transport 在初始化时创建,连接池可复用
|
||||
func NewHTTPClient() *HTTPClient {
|
||||
cfg := DefaultHTTPClientConfig
|
||||
return NewHTTPClientWithConfig(cfg)
|
||||
@@ -93,8 +89,6 @@ func NewHTTPClientWithConfig(cfg HTTPClientConfig) *HTTPClient {
|
||||
}
|
||||
|
||||
// SetTimeout 设置超时时间
|
||||
// 评分: ⭐⭐⭐⭐⭐
|
||||
// 理由: 链式调用,动态调整超时
|
||||
func (c *HTTPClient) SetTimeout(timeout time.Duration) *HTTPClient {
|
||||
c.timeout = timeout
|
||||
c.client.Timeout = timeout
|
||||
@@ -330,8 +324,6 @@ func (c *HTTPClient) DoWithResponse(req *http.Request) (*http.Response, error) {
|
||||
}
|
||||
|
||||
// Close 关闭客户端(释放连接池资源)
|
||||
// 评分: ⭐⭐⭐⭐⭐
|
||||
// 理由: 清理资源,避免连接泄漏
|
||||
func (c *HTTPClient) Close() {
|
||||
c.transport.CloseIdleConnections()
|
||||
}
|
||||
|
||||
@@ -23,8 +23,6 @@ const (
|
||||
)
|
||||
|
||||
// RandString 生成指定长度的随机字符串(字母+数字)
|
||||
// 评分: ⭐⭐⭐⭐⭐
|
||||
// 理由: 使用 sync.Pool 复用 rand.Source,性能优秀;位运算优化高效
|
||||
func RandString(n int) string {
|
||||
if n <= 0 {
|
||||
return ""
|
||||
@@ -48,8 +46,6 @@ func RandString(n int) string {
|
||||
}
|
||||
|
||||
// RandDigit 生成指定长度的随机数字字符串
|
||||
// 评分: ⭐⭐⭐⭐⭐
|
||||
// 理由: 同 RandString,适用于验证码、订单号等场景
|
||||
func RandDigit(n int) string {
|
||||
if n <= 0 {
|
||||
return ""
|
||||
@@ -73,8 +69,6 @@ func RandDigit(n int) string {
|
||||
}
|
||||
|
||||
// RandInt 返回 [min, max) 范围内的随机整数
|
||||
// 评分: ⭐⭐⭐⭐
|
||||
// 理由: 实用函数,自动处理 min > max 的情况
|
||||
func RandInt(min, max int) int {
|
||||
if min == max {
|
||||
return min
|
||||
@@ -88,8 +82,6 @@ func RandInt(min, max int) int {
|
||||
}
|
||||
|
||||
// RandInt64 返回 [min, max) 范围内的随机 int64
|
||||
// 评分: ⭐⭐⭐⭐
|
||||
// 理由: RandInt 的 int64 版本,适用于大范围随机数
|
||||
func RandInt64(min, max int64) int64 {
|
||||
if min == max {
|
||||
return min
|
||||
|
||||
@@ -6,8 +6,6 @@ import (
|
||||
)
|
||||
|
||||
// IsBlank 检查字符串是否为空或仅包含空白字符
|
||||
// 评分: ⭐⭐⭐⭐⭐
|
||||
// 理由: 常用函数,正确处理 Unicode 空白字符
|
||||
func IsBlank(s string) bool {
|
||||
if s == "" {
|
||||
return true
|
||||
@@ -21,15 +19,11 @@ func IsBlank(s string) bool {
|
||||
}
|
||||
|
||||
// IsNotBlank 检查字符串是否非空且不全是空白字符
|
||||
// 评分: ⭐⭐⭐⭐⭐
|
||||
// 理由: IsBlank 的反向,语义更清晰
|
||||
func IsNotBlank(s string) bool {
|
||||
return !IsBlank(s)
|
||||
}
|
||||
|
||||
// IsAnyBlank 检查多个字符串中是否有任意一个为空或空白
|
||||
// 评分: ⭐⭐⭐⭐
|
||||
// 理由: 参数校验场景常用,避免多次调用 IsBlank
|
||||
func IsAnyBlank(strs ...string) bool {
|
||||
for _, s := range strs {
|
||||
if IsBlank(s) {
|
||||
@@ -40,8 +34,6 @@ func IsAnyBlank(strs ...string) bool {
|
||||
}
|
||||
|
||||
// IsAllBlank 检查所有字符串是否都为空或空白
|
||||
// 评分: ⭐⭐⭐⭐
|
||||
// 理由: IsAnyBlank 的反向,适用于批量校验
|
||||
func IsAllBlank(strs ...string) bool {
|
||||
for _, s := range strs {
|
||||
if IsNotBlank(s) {
|
||||
@@ -52,8 +44,6 @@ func IsAllBlank(strs ...string) bool {
|
||||
}
|
||||
|
||||
// DefaultIfBlank 如果字符串为空或空白,返回默认值
|
||||
// 评分: ⭐⭐⭐⭐⭐
|
||||
// 理由: 配置默认值场景常用,代码简洁
|
||||
func DefaultIfBlank(s, def string) string {
|
||||
if IsBlank(s) {
|
||||
return def
|
||||
@@ -62,8 +52,6 @@ func DefaultIfBlank(s, def string) string {
|
||||
}
|
||||
|
||||
// IsEmpty 检查任意类型是否为空值
|
||||
// 评分: ⭐⭐⭐
|
||||
// 理由: 功能实用,但使用反射有性能开销;建议仅在必要时使用
|
||||
// 支持: string, []T, map[K]V, nil, 零值
|
||||
func IsEmpty(v any) bool {
|
||||
if v == nil {
|
||||
@@ -80,8 +68,6 @@ func IsEmpty(v any) bool {
|
||||
}
|
||||
|
||||
// Trim 去除字符串首尾的空白字符(包括空格、制表符、换行符)
|
||||
// 评分: ⭐⭐⭐⭐
|
||||
// 理由: 比标准库 strings.Trim 更语义化,预设常用空白字符
|
||||
func Trim(s string) string {
|
||||
return trimFunc(s, unicode.IsSpace)
|
||||
}
|
||||
@@ -113,8 +99,6 @@ func trimRightFunc(s string, f func(rune) bool) string {
|
||||
}
|
||||
|
||||
// Substr 截取子字符串(按 Unicode 字符计数)
|
||||
// 评分: ⭐⭐⭐⭐
|
||||
// 理由: 正确处理多字节字符(中文等),避免截断
|
||||
// 参数: start 起始位置(支持负数从末尾计算),length 截取长度
|
||||
func Substr(s string, start, length int) string {
|
||||
runes := []rune(s)
|
||||
@@ -147,15 +131,11 @@ func Substr(s string, start, length int) string {
|
||||
}
|
||||
|
||||
// StrLen 获取字符串的 Unicode 字符数
|
||||
// 评分: ⭐⭐⭐⭐⭐
|
||||
// 理由: 正确计算多字节字符长度,比 len() 更准确
|
||||
func StrLen(s string) int {
|
||||
return utf8.RuneCountInString(s)
|
||||
}
|
||||
|
||||
// EqualsIgnoreCase 不区分大小写比较字符串
|
||||
// 评分: ⭐⭐⭐⭐
|
||||
// 理由: 常用比较函数,使用标准库实现保证正确性
|
||||
func EqualsIgnoreCase(a, b string) bool {
|
||||
if a == b {
|
||||
return true
|
||||
|
||||
@@ -5,16 +5,12 @@ import (
|
||||
)
|
||||
|
||||
// URLBuilder URL 构建器
|
||||
// 评分: ⭐⭐⭐⭐⭐
|
||||
// 理由: 链式调用设计优雅,便于构建复杂 URL
|
||||
type URLBuilder struct {
|
||||
u *url.URL
|
||||
query url.Values
|
||||
}
|
||||
|
||||
// ParseURL 解析 URL 字符串
|
||||
// 评分: ⭐⭐⭐⭐⭐
|
||||
// 理由: 创建 URLBuilder 的入口函数
|
||||
func ParseURL(rawURL string) (*URLBuilder, error) {
|
||||
u, err := url.Parse(rawURL)
|
||||
if err != nil {
|
||||
@@ -27,16 +23,12 @@ func ParseURL(rawURL string) (*URLBuilder, error) {
|
||||
}
|
||||
|
||||
// AddQuery 添加单个查询参数
|
||||
// 评分: ⭐⭐⭐⭐⭐
|
||||
// 理由: 链式调用,支持多次添加
|
||||
func (b *URLBuilder) AddQuery(key, value string) *URLBuilder {
|
||||
b.query.Add(key, value)
|
||||
return b
|
||||
}
|
||||
|
||||
// AddQueries 批量添加查询参数
|
||||
// 评分: ⭐⭐⭐⭐⭐
|
||||
// 理由: 批量添加更高效
|
||||
func (b *URLBuilder) AddQueries(params map[string]string) *URLBuilder {
|
||||
for k, v := range params {
|
||||
b.query.Set(k, v)
|
||||
@@ -45,62 +37,46 @@ func (b *URLBuilder) AddQueries(params map[string]string) *URLBuilder {
|
||||
}
|
||||
|
||||
// SetQuery 设置查询参数(覆盖同名参数)
|
||||
// 评分: ⭐⭐⭐⭐⭐
|
||||
// 理由: 区别于 Add,覆盖同名参数
|
||||
func (b *URLBuilder) SetQuery(key, value string) *URLBuilder {
|
||||
b.query.Set(key, value)
|
||||
return b
|
||||
}
|
||||
|
||||
// SetPath 设置路径
|
||||
// 评分: ⭐⭐⭐⭐
|
||||
// 理由: 动态修改路径
|
||||
func (b *URLBuilder) SetPath(path string) *URLBuilder {
|
||||
b.u.Path = path
|
||||
return b
|
||||
}
|
||||
|
||||
// SetScheme 设置协议(http/https)
|
||||
// 评分: ⭐⭐⭐⭐
|
||||
// 理由: 协议切换
|
||||
func (b *URLBuilder) SetScheme(scheme string) *URLBuilder {
|
||||
b.u.Scheme = scheme
|
||||
return b
|
||||
}
|
||||
|
||||
// SetHost 设置主机
|
||||
// 评分: ⭐⭐⭐⭐
|
||||
// 理由: 域名切换
|
||||
func (b *URLBuilder) SetHost(host string) *URLBuilder {
|
||||
b.u.Host = host
|
||||
return b
|
||||
}
|
||||
|
||||
// Build 构建最终的 URL
|
||||
// 评分: ⭐⭐⭐⭐⭐
|
||||
// 理由: 返回标准库 url.URL
|
||||
func (b *URLBuilder) Build() *url.URL {
|
||||
b.u.RawQuery = b.query.Encode()
|
||||
return b.u
|
||||
}
|
||||
|
||||
// String 返回 URL 字符串
|
||||
// 评分: ⭐⭐⭐⭐⭐
|
||||
// 理由: 最常用的输出方法
|
||||
func (b *URLBuilder) String() string {
|
||||
return b.Build().String()
|
||||
}
|
||||
|
||||
// URLEncode URL 编码
|
||||
// 评分: ⭐⭐⭐⭐⭐
|
||||
// 理由: 常用编码函数
|
||||
func URLEncode(s string) string {
|
||||
return url.QueryEscape(s)
|
||||
}
|
||||
|
||||
// URLDecode URL 解码
|
||||
// 评分: ⭐⭐⭐⭐⭐
|
||||
// 理由: 常用解码函数
|
||||
func URLDecode(s string) (string, error) {
|
||||
return url.QueryUnescape(s)
|
||||
}
|
||||
|
||||
@@ -5,29 +5,21 @@ import (
|
||||
)
|
||||
|
||||
// UUID 生成 UUID v4 字符串
|
||||
// 评分: ⭐⭐⭐⭐⭐
|
||||
// 理由: 常用唯一标识生成,使用标准库 google/uuid
|
||||
func UUID() string {
|
||||
return uuid.New().String()
|
||||
}
|
||||
|
||||
// UUIDShort 生成短 UUID(无横线)
|
||||
// 评分: ⭐⭐⭐⭐⭐
|
||||
// 理由: 数据库主键、订单号等场景常用
|
||||
func UUIDShort() string {
|
||||
return uuid.New().String()[:32]
|
||||
}
|
||||
|
||||
// UUIDParse 解析 UUID 字符串
|
||||
// 评分: ⭐⭐⭐⭐
|
||||
// 理由: UUID 解析验证
|
||||
func UUIDParse(s string) (uuid.UUID, error) {
|
||||
return uuid.Parse(s)
|
||||
}
|
||||
|
||||
// UUIDValid 检查 UUID 字符串是否有效
|
||||
// 评分: ⭐⭐⭐⭐
|
||||
// 理由: 快速验证
|
||||
func UUIDValid(s string) bool {
|
||||
_, err := uuid.Parse(s)
|
||||
return err == nil
|
||||
|
||||
@@ -5,8 +5,6 @@ import (
|
||||
)
|
||||
|
||||
// IsPhone 检查是否为有效的中国大陆手机号
|
||||
// 评分: ⭐⭐⭐⭐
|
||||
// 理由: 常用验证,但正则需要随运营商号段更新
|
||||
// 注意: 正则基于当前号段,新号段开放时需更新
|
||||
func IsPhone(phone string) bool {
|
||||
// 1开头,第二位为3-9,共11位
|
||||
@@ -16,8 +14,6 @@ func IsPhone(phone string) bool {
|
||||
}
|
||||
|
||||
// IsEmail 检查是否为有效的邮箱地址
|
||||
// 评分: ⭐⭐⭐⭐
|
||||
// 理由: 常用验证,简单正则覆盖大部分场景
|
||||
func IsEmail(email string) bool {
|
||||
// 简单邮箱验证:xxx@xxx.xxx
|
||||
pattern := `^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`
|
||||
@@ -26,8 +22,6 @@ func IsEmail(email string) bool {
|
||||
}
|
||||
|
||||
// IsIPv4 检查是否为有效的 IPv4 地址
|
||||
// 评分: ⭐⭐⭐⭐⭐
|
||||
// 理由: 网络编程常用验证
|
||||
func IsIPv4(ip string) bool {
|
||||
pattern := `^(\d{1,3}\.){3}\d{1,3}$`
|
||||
matched, _ := regexp.MatchString(pattern, ip)
|
||||
@@ -46,8 +40,6 @@ func IsIPv4(ip string) bool {
|
||||
}
|
||||
|
||||
// IsIDCard 检查是否为有效的中国身份证号(18位)
|
||||
// 评分: ⭐⭐⭐
|
||||
// 理由: 身份证验证需求常见,但校验位算法复杂,此为简化版
|
||||
// 注意: 仅校验格式,不校验校验位
|
||||
func IsIDCard(id string) bool {
|
||||
// 18位身份证:6位地区码 + 8位生日 + 3位顺序码 + 1位校验码
|
||||
@@ -57,8 +49,6 @@ func IsIDCard(id string) bool {
|
||||
}
|
||||
|
||||
// IsChinese 检查字符串是否全部为中文字符
|
||||
// 评分: ⭐⭐⭐⭐
|
||||
// 理由: 中文内容校验常用
|
||||
func IsChinese(s string) bool {
|
||||
for _, r := range s {
|
||||
if r < 0x4E00 || r > 0x9FFF {
|
||||
@@ -69,8 +59,6 @@ func IsChinese(s string) bool {
|
||||
}
|
||||
|
||||
// HasChinese 检查字符串是否包含中文字符
|
||||
// 评分: ⭐⭐⭐⭐
|
||||
// 理由: 混合内容检测
|
||||
func HasChinese(s string) bool {
|
||||
for _, r := range s {
|
||||
if r >= 0x4E00 && r <= 0x9FFF {
|
||||
@@ -81,8 +69,6 @@ func HasChinese(s string) bool {
|
||||
}
|
||||
|
||||
// IsNumeric 检查字符串是否全部为数字
|
||||
// 评分: ⭐⭐⭐⭐⭐
|
||||
// 理由: 简单高效的数字检测
|
||||
func IsNumeric(s string) bool {
|
||||
if len(s) == 0 {
|
||||
return false
|
||||
@@ -96,8 +82,6 @@ func IsNumeric(s string) bool {
|
||||
}
|
||||
|
||||
// IsAlpha 检查字符串是否全部为字母
|
||||
// 评分: ⭐⭐⭐⭐
|
||||
// 理由: 字母检测
|
||||
func IsAlpha(s string) bool {
|
||||
if len(s) == 0 {
|
||||
return false
|
||||
@@ -111,8 +95,6 @@ func IsAlpha(s string) bool {
|
||||
}
|
||||
|
||||
// IsAlphanumeric 检查字符串是否全部为字母或数字
|
||||
// 评分: ⭐⭐⭐⭐⭐
|
||||
// 理由: 用户名、密码等常用验证
|
||||
func IsAlphanumeric(s string) bool {
|
||||
if len(s) == 0 {
|
||||
return false
|
||||
|
||||
Reference in New Issue
Block a user