Files

132 lines
3.3 KiB
Go

package numrange
import (
"context"
"fmt"
"math/big"
"strings"
"unicode"
"github.com/gogf/gf/v2/errors/gerror"
)
func Build(ctx context.Context, start, end, field string, maxRows int) ([]string, error) {
start = strings.TrimSpace(start)
end = strings.TrimSpace(end)
if start == "" || end == "" {
return nil, gerror.Newf("%s起止号不能为空", field)
}
if strings.EqualFold(strings.TrimSpace(field), "ICCID") {
return buildAlphaNumericICCIDRange(ctx, start, end, field, maxRows)
}
return buildNumericRange(ctx, start, end, field, maxRows)
}
func buildNumericRange(ctx context.Context, start, end, field string, maxRows int) ([]string, error) {
if !isDigits(start) || !isDigits(end) {
return nil, gerror.Newf("%s起止号必须为数字", field)
}
if len(start) != len(end) {
return nil, gerror.Newf("%s起止号长度必须一致", field)
}
startInt, ok := new(big.Int).SetString(start, 10)
if !ok {
return nil, gerror.Newf("%s起始号格式错误", field)
}
endInt, ok := new(big.Int).SetString(end, 10)
if !ok {
return nil, gerror.Newf("%s结束号格式错误", field)
}
if startInt.Cmp(endInt) > 0 {
return nil, gerror.Newf("%s起始号不能大于结束号", field)
}
count := new(big.Int).Sub(endInt, startInt)
count.Add(count, big.NewInt(1))
if maxRows > 0 && (!count.IsInt64() || count.Int64() > int64(maxRows)) {
return nil, gerror.Newf("%s号段数量不能超过%d条", field, maxRows)
}
size := int(count.Int64())
rows := make([]string, 0, size)
current := new(big.Int).Set(startInt)
one := big.NewInt(1)
for i := 0; i < size; i++ {
if err := ctx.Err(); err != nil {
return nil, err
}
rows = append(rows, fmt.Sprintf("%0*s", len(start), current.String()))
current.Add(current, one)
}
return rows, nil
}
func buildAlphaNumericICCIDRange(ctx context.Context, start, end, field string, maxRows int) ([]string, error) {
if !isAlphaNumeric(start) || !isAlphaNumeric(end) {
return nil, gerror.Newf("%s起止号只能包含字母和数字", field)
}
if len(start) != len(end) {
return nil, gerror.Newf("%s起止号长度必须一致", field)
}
if start == end {
return []string{start}, nil
}
startPrefix, startTail := splitTrailingDigits(start)
endPrefix, endTail := splitTrailingDigits(end)
if startTail == "" || endTail == "" {
return nil, gerror.Newf("%s起止号必须包含可递增的数字尾号", field)
}
if startPrefix != endPrefix {
return nil, gerror.Newf("%s起止号前缀必须一致", field)
}
tails, err := buildNumericRange(ctx, startTail, endTail, field, maxRows)
if err != nil {
return nil, err
}
rows := make([]string, 0, len(tails))
for _, tail := range tails {
rows = append(rows, startPrefix+tail)
}
return rows, nil
}
func isDigits(value string) bool {
if value == "" {
return false
}
for _, r := range value {
if !unicode.IsDigit(r) {
return false
}
}
return true
}
func isAlphaNumeric(value string) bool {
if value == "" {
return false
}
for _, r := range value {
if (r >= '0' && r <= '9') || (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') {
continue
}
return false
}
return true
}
func splitTrailingDigits(value string) (prefix, digits string) {
index := len(value)
for index > 0 {
ch := value[index-1]
if ch < '0' || ch > '9' {
break
}
index--
}
return value[:index], value[index:]
}