82 lines
2.1 KiB
Go
82 lines
2.1 KiB
Go
package utils
|
||
|
||
import (
|
||
"fmt"
|
||
"strings"
|
||
"time"
|
||
|
||
"github.com/oklog/ulid/v2"
|
||
)
|
||
|
||
// ShardingNoConfig 分表单号配置
|
||
type ShardingNoConfig struct {
|
||
Prefix string // 单号前缀,如 "ORD"、"PAY"
|
||
DateFormat string // 日期格式,如 "200601"(年月)、"20060102"(年月日)
|
||
}
|
||
|
||
// 预定义配置
|
||
var (
|
||
// OrderNoConfig 订单号配置(ORD + YYYYMM + ULID)
|
||
OrderNoConfig = ShardingNoConfig{
|
||
Prefix: "ORD",
|
||
DateFormat: "200601", // YYYYMM
|
||
}
|
||
|
||
// PaymentNoConfig 支付单号配置(PAY + YYYYMMDD + ULID)
|
||
PaymentNoConfig = ShardingNoConfig{
|
||
Prefix: "PAY",
|
||
DateFormat: "20060102", // YYYYMMDD
|
||
}
|
||
)
|
||
|
||
// GenerateShardingNo 生成分表单号
|
||
// 格式:前缀 + 日期 + ULID
|
||
// 例如:ORD20250101ARZ3S0K5M2X9P4Q6R8T1V3W5Y7Z9
|
||
func GenerateShardingNo(config ShardingNoConfig) string {
|
||
now := time.Now().UTC()
|
||
dateStr := now.Format(config.DateFormat)
|
||
ulidStr := ulid.Make().String()
|
||
return fmt.Sprintf("%s%s%s", config.Prefix, dateStr, ulidStr)
|
||
}
|
||
|
||
// ParseShardingNoDate 从分表单号解析日期
|
||
// 返回:解析的时间和是否成功
|
||
func ParseShardingNoDate(no string, config ShardingNoConfig) (time.Time, bool) {
|
||
prefixLen := len(config.Prefix)
|
||
dateLen := len(config.DateFormat)
|
||
minLen := prefixLen + dateLen + 1 // 前缀 + 日期 + 至少1位ULID
|
||
|
||
// 验证长度和前缀
|
||
if len(no) < minLen || !strings.HasPrefix(no, config.Prefix) {
|
||
return time.Time{}, false
|
||
}
|
||
|
||
// 提取日期部分
|
||
dateStr := no[prefixLen : prefixLen+dateLen]
|
||
|
||
// 验证日期格式(简单验证:都是数字)
|
||
for _, c := range dateStr {
|
||
if c < '0' || c > '9' {
|
||
return time.Time{}, false
|
||
}
|
||
}
|
||
|
||
// 解析日期
|
||
parsedTime, err := time.Parse(config.DateFormat, dateStr)
|
||
if err != nil {
|
||
return time.Time{}, false
|
||
}
|
||
|
||
return parsedTime, true
|
||
}
|
||
|
||
// ParseShardingNoYearMonth 从分表单号解析年月字符串(用于分表定位)
|
||
// 返回:年月字符串(如 "202501")和是否成功
|
||
func ParseShardingNoYearMonth(no string, config ShardingNoConfig) (string, bool) {
|
||
parsedTime, ok := ParseShardingNoDate(no, config)
|
||
if !ok {
|
||
return "", false
|
||
}
|
||
return parsedTime.Format("200601"), true
|
||
}
|