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:] }