diff --git a/cache/redis/cache.go b/cache/redis/cache.go
index 6645b0f..a598ba3 100644
--- a/cache/redis/cache.go
+++ b/cache/redis/cache.go
@@ -5,6 +5,7 @@ package redis
import (
"encoding/json"
"fmt"
+ "strings"
"time"
"git.apinb.com/bsm-sdk/core/errcode"
@@ -15,19 +16,20 @@ import (
// prefix: 键前缀
// params: 键参数
// 返回: 完整的缓存键
-func (c *RedisClient) BuildKey(prefix string, params ...interface{}) string {
- key := vars.CacheKeyPrefix + prefix
+func (c *RedisClient) BuildKey(prefix string, params ...any) string {
+ var key strings.Builder
+ key.WriteString(vars.CacheKeyPrefix + prefix)
for _, param := range params {
- key += fmt.Sprintf(":%v", param)
+ key.WriteString(fmt.Sprintf(":%v", param))
}
- return key
+ return key.String()
}
// Get 获取缓存
// key: 缓存键
// result: 存储结果的指针
// 返回: 错误信息
-func (c *RedisClient) Get(key string, result interface{}) error {
+func (c *RedisClient) Get(key string, result any) error {
if c.Client == nil {
return errcode.ErrRedis
}
@@ -45,7 +47,7 @@ func (c *RedisClient) Get(key string, result interface{}) error {
// value: 缓存值
// ttl: 过期时间
// 返回: 错误信息
-func (c *RedisClient) Set(key string, value interface{}, ttl time.Duration) error {
+func (c *RedisClient) Set(key string, value any, ttl time.Duration) error {
if c.Client == nil {
return errcode.ErrRedis
}
diff --git a/crypto/token/jwt.go b/crypto/token/jwt.go
index 6f85631..7e5d085 100644
--- a/crypto/token/jwt.go
+++ b/crypto/token/jwt.go
@@ -62,7 +62,7 @@ func (t *tokenJwt) GenerateJwt(id uint, identity, client, role string, owner any
// 解析JWT
func (t *tokenJwt) ParseJwt(tokenstring string) (*Claims, error) {
- token, err := jwt.ParseWithClaims(tokenstring, &Claims{}, func(token *jwt.Token) (interface{}, error) {
+ token, err := jwt.ParseWithClaims(tokenstring, &Claims{}, func(token *jwt.Token) (any, error) {
return []byte(t.SecretKey), nil
})
if claims, ok := token.Claims.(*Claims); ok && token.Valid {
diff --git a/database/elastic/elasticsearch.go b/database/elastic/elasticsearch.go
index dbbefa5..95e0912 100644
--- a/database/elastic/elasticsearch.go
+++ b/database/elastic/elasticsearch.go
@@ -44,7 +44,7 @@ func NewElastic(endpoints []string, username, password string) (*ES, error) {
// "time": time.Now().Unix(),
// "date": time.Now(),
// }
-func (es *ES) CreateDocument(index string, id string, doc *interface{}) {
+func (es *ES) CreateDocument(index string, id string, doc *any) {
var buf bytes.Buffer
if err := json.NewEncoder(&buf).Encode(doc); err != nil {
log.Println("Elastic NewEncoder:", err)
@@ -75,7 +75,7 @@ func (es *ES) CreateDocument(index string, id string, doc *interface{}) {
// index 如果文档不存在就创建,如果文档存在就更新
// update 更新一个文档,如果文档不存在就返回错误
// delete 删除一个文档,如果要删除的文档id不存在,就返回错误
-func (es *ES) Batch(index string, documens []map[string]interface{}, action string) {
+func (es *ES) Batch(index string, documens []map[string]any, action string) {
log.SetFlags(0)
var (
@@ -162,7 +162,7 @@ func (es *ES) Batch(index string, documens []map[string]interface{}, action stri
}
}
-func (es *ES) Search(index string, query map[string]interface{}) (res *esapi.Response, err error) {
+func (es *ES) Search(index string, query map[string]any) (res *esapi.Response, err error) {
var buf bytes.Buffer
if err = json.NewEncoder(&buf).Encode(query); err != nil {
return
@@ -201,7 +201,7 @@ func (es *ES) Delete(index, idx string) (res *esapi.Response, err error) {
return
}
-func (es *ES) DeleteByQuery(index []string, query map[string]interface{}) (res *esapi.Response, err error) {
+func (es *ES) DeleteByQuery(index []string, query map[string]any) (res *esapi.Response, err error) {
var buf bytes.Buffer
if err = json.NewEncoder(&buf).Encode(query); err != nil {
return
diff --git a/go.mod b/go.mod
index f750145..1aa22f2 100644
--- a/go.mod
+++ b/go.mod
@@ -1,3 +1,3 @@
module git.apinb.com/bsm-sdk/core
-go 1.25.1
+go 1.26.0
diff --git a/licence/licence.go b/licence/licence.go
index 6821553..52dc6b1 100644
--- a/licence/licence.go
+++ b/licence/licence.go
@@ -14,6 +14,7 @@ import (
"os"
"path"
"path/filepath"
+ "slices"
"sort"
"strconv"
"strings"
@@ -135,13 +136,7 @@ func (l *Licence) VerifyLicence(licName string) bool {
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
// 检查机器码是否存在授权列表中
func (l *Licence) ValidMachineCode(code string) bool {
- result := false
- for _, c := range l.MachineCodes {
- if c == code {
- result = true
- break
- }
- }
+ result := slices.Contains(l.MachineCodes, code)
return result
}
@@ -265,7 +260,7 @@ func getMacAddrs() []string {
if err != nil {
return macs
}
- for i := 0; i < len(netfaces); i++ {
+ for i := range netfaces {
if (netfaces[i].Flags&net.FlagUp) != 0 && (netfaces[i].Flags&net.FlagLoopback) == 0 {
addrs, _ := netfaces[i].Addrs()
for _, address := range addrs {
diff --git a/logger/logger.go b/logger/logger.go
index b1a5956..867bcca 100644
--- a/logger/logger.go
+++ b/logger/logger.go
@@ -159,7 +159,7 @@ func (l *Logger) sendToRemote(level, name, out string) {
if l.endpoint == "" {
return
}
- data := map[string]interface{}{
+ data := map[string]any{
"level": level,
"name": name,
"out": out,
@@ -169,7 +169,7 @@ func (l *Logger) sendToRemote(level, name, out string) {
}
// Debug 输出调试信息
-func (l *Logger) Debug(v ...interface{}) {
+func (l *Logger) Debug(v ...any) {
if l.level <= vars.DEBUG {
l.checkAndRotateLog()
out := fmt.Sprint(v...)
@@ -181,7 +181,7 @@ func (l *Logger) Debug(v ...interface{}) {
}
// Debugf 格式化输出调试信息
-func (l *Logger) Debugf(format string, v ...interface{}) {
+func (l *Logger) Debugf(format string, v ...any) {
if l.level <= vars.DEBUG {
l.checkAndRotateLog()
out := fmt.Sprintf(format, v...)
@@ -193,7 +193,7 @@ func (l *Logger) Debugf(format string, v ...interface{}) {
}
// Info 输出信息
-func (l *Logger) Info(v ...interface{}) {
+func (l *Logger) Info(v ...any) {
if l.level <= vars.INFO {
l.checkAndRotateLog()
out := fmt.Sprint(v...)
@@ -205,7 +205,7 @@ func (l *Logger) Info(v ...interface{}) {
}
// Infof 格式化输出信息
-func (l *Logger) Infof(format string, v ...interface{}) {
+func (l *Logger) Infof(format string, v ...any) {
if l.level <= vars.INFO {
l.checkAndRotateLog()
out := fmt.Sprintf(format, v...)
@@ -217,7 +217,7 @@ func (l *Logger) Infof(format string, v ...interface{}) {
}
// Warn 输出警告
-func (l *Logger) Warn(v ...interface{}) {
+func (l *Logger) Warn(v ...any) {
if l.level <= vars.WARN {
l.checkAndRotateLog()
out := fmt.Sprint(v...)
@@ -229,7 +229,7 @@ func (l *Logger) Warn(v ...interface{}) {
}
// Warnf 格式化输出警告
-func (l *Logger) Warnf(format string, v ...interface{}) {
+func (l *Logger) Warnf(format string, v ...any) {
if l.level <= vars.WARN {
l.checkAndRotateLog()
out := fmt.Sprintf(format, v...)
@@ -241,7 +241,7 @@ func (l *Logger) Warnf(format string, v ...interface{}) {
}
// Error 输出错误
-func (l *Logger) Error(v ...interface{}) {
+func (l *Logger) Error(v ...any) {
if l.level <= vars.ERROR {
l.checkAndRotateLog()
out := fmt.Sprint(v...)
@@ -253,7 +253,7 @@ func (l *Logger) Error(v ...interface{}) {
}
// Errorf 格式化输出错误
-func (l *Logger) Errorf(format string, v ...interface{}) {
+func (l *Logger) Errorf(format string, v ...any) {
if l.level <= vars.ERROR {
l.checkAndRotateLog()
out := fmt.Sprintf(format, v...)
@@ -265,7 +265,7 @@ func (l *Logger) Errorf(format string, v ...interface{}) {
}
// Fatal 输出致命错误并退出程序
-func (l *Logger) Fatal(v ...interface{}) {
+func (l *Logger) Fatal(v ...any) {
l.checkAndRotateLog()
out := fmt.Sprint(v...)
if l.onRemote {
@@ -276,7 +276,7 @@ func (l *Logger) Fatal(v ...interface{}) {
}
// Fatalf 格式化输出致命错误并退出程序
-func (l *Logger) Fatalf(format string, v ...interface{}) {
+func (l *Logger) Fatalf(format string, v ...any) {
l.checkAndRotateLog()
out := fmt.Sprintf(format, v...)
if l.onRemote {
@@ -287,17 +287,17 @@ func (l *Logger) Fatalf(format string, v ...interface{}) {
}
// Print 输出信息(兼容标准log包)
-func (l *Logger) Print(v ...interface{}) {
+func (l *Logger) Print(v ...any) {
l.Info(v...)
}
// Printf 格式化输出信息(兼容标准log包)
-func (l *Logger) Printf(format string, v ...interface{}) {
+func (l *Logger) Printf(format string, v ...any) {
l.Infof(format, v...)
}
// Println 输出信息并换行(兼容标准log包)
-func (l *Logger) Println(v ...interface{}) {
+func (l *Logger) Println(v ...any) {
l.Info(v...)
}
@@ -326,91 +326,91 @@ func (l *Logger) Close() error {
// 全局日志函数(兼容标准log包)
// Debug 全局调试日志
-func Debug(v ...interface{}) {
+func Debug(v ...any) {
if globalLogger != nil {
globalLogger.Debug(v...)
}
}
// Debugf 全局调试日志
-func Debugf(format string, v ...interface{}) {
+func Debugf(format string, v ...any) {
if globalLogger != nil {
globalLogger.Debugf(format, v...)
}
}
// Info 全局信息日志
-func Info(v ...interface{}) {
+func Info(v ...any) {
if globalLogger != nil {
globalLogger.Info(v...)
}
}
// Infof 全局信息日志
-func Infof(format string, v ...interface{}) {
+func Infof(format string, v ...any) {
if globalLogger != nil {
globalLogger.Infof(format, v...)
}
}
// Warn 全局警告日志
-func Warn(v ...interface{}) {
+func Warn(v ...any) {
if globalLogger != nil {
globalLogger.Warn(v...)
}
}
// Warnf 全局警告日志
-func Warnf(format string, v ...interface{}) {
+func Warnf(format string, v ...any) {
if globalLogger != nil {
globalLogger.Warnf(format, v...)
}
}
// Error 全局错误日志
-func Error(v ...interface{}) {
+func Error(v ...any) {
if globalLogger != nil {
globalLogger.Error(v...)
}
}
// Errorf 全局错误日志
-func Errorf(format string, v ...interface{}) {
+func Errorf(format string, v ...any) {
if globalLogger != nil {
globalLogger.Errorf(format, v...)
}
}
// Fatal 全局致命错误日志
-func Fatal(v ...interface{}) {
+func Fatal(v ...any) {
if globalLogger != nil {
globalLogger.Fatal(v...)
}
}
// Fatalf 全局致命错误日志
-func Fatalf(format string, v ...interface{}) {
+func Fatalf(format string, v ...any) {
if globalLogger != nil {
globalLogger.Fatalf(format, v...)
}
}
// Print 全局打印日志(兼容标准log包)
-func Print(v ...interface{}) {
+func Print(v ...any) {
if globalLogger != nil {
globalLogger.Print(v...)
}
}
// Printf 全局打印日志(兼容标准log包)
-func Printf(format string, v ...interface{}) {
+func Printf(format string, v ...any) {
if globalLogger != nil {
globalLogger.Printf(format, v...)
}
}
// Println 全局打印日志(兼容标准log包)
-func Println(v ...interface{}) {
+func Println(v ...any) {
if globalLogger != nil {
globalLogger.Println(v...)
}
diff --git a/printer/print.go b/printer/print.go
index c409d33..de69be1 100644
--- a/printer/print.go
+++ b/printer/print.go
@@ -16,25 +16,25 @@ func init() {
}
// record INFO message. Color White
-func Info(format string, a ...interface{}) {
+func Info(format string, a ...any) {
message := fmt.Sprintf("\033[37m[Info] "+format+"\033[0m\n", a...)
logger.Print(message)
}
// record Warn message. Color Orange
-func Warn(format string, a ...interface{}) {
+func Warn(format string, a ...any) {
message := fmt.Sprintf("\033[33m[Warn] "+format+"\033[0m\n", a...)
logger.Print(message)
}
// record Success message. Color Green
-func Success(format string, a ...interface{}) {
+func Success(format string, a ...any) {
message := fmt.Sprintf("\033[32m[Succ] "+format+"\033[0m\n", a...)
logger.Print(message)
}
// record ERROR message. Color Red
-func Error(format string, a ...interface{}) {
+func Error(format string, a ...any) {
message := fmt.Sprintf("\033[31m[Error] "+format+"\033[0m\n", a...)
logger.Print(message)
}
diff --git a/third/wechat.go b/third/wechat.go
index 3357e66..72a4f73 100644
--- a/third/wechat.go
+++ b/third/wechat.go
@@ -56,7 +56,7 @@ func WeChat_Pkcs7Unpad(data []byte, blockSize int) ([]byte, error) {
if n == 0 || n > len(data) {
return nil, ErrInvalidPKCS7Padding
}
- for i := 0; i < n; i++ {
+ for i := range n {
if data[len(data)-n+i] != c {
return nil, ErrInvalidPKCS7Padding
}
diff --git a/utils/array.go b/utils/array.go
index 49b6491..22ceb18 100644
--- a/utils/array.go
+++ b/utils/array.go
@@ -1,5 +1,7 @@
package utils
+import "slices"
+
import "strings"
// ArrayInString 判断字符串是否存在于字符串切片中
@@ -7,24 +9,14 @@ import "strings"
// array: 需要查找的字符串切片
func ArrayInString(target string, array []string) bool {
target = strings.TrimSpace(target)
- for _, v := range array {
- if strings.TrimSpace(v) == target {
- return true
- }
- }
- return false
+ return slices.Contains(array, target)
}
// ArrayInInt 判断整数是否存在于整型切片中
// target: 待匹配的目标整数
// array: 需要查找的整型切片
func ArrayInInt(target int, array []int) bool {
- for _, v := range array {
- if v == target {
- return true
- }
- }
- return false
+ return slices.Contains(array, target)
}
// ArrayRemoveRepeatString 去除字符串切片中的重复元素(保持原有顺序)
diff --git a/utils/convert.go b/utils/convert.go
index ed9b7e0..56d5cbd 100644
--- a/utils/convert.go
+++ b/utils/convert.go
@@ -103,7 +103,7 @@ func BinaryToDecimal(bit string) (num int) {
fields := strings.Split(bit, "")
lens := len(fields)
var tempF float64 = 0
- for i := 0; i < lens; i++ {
+ for i := range lens {
floatNum := String2Float64(fields[i])
tempF += floatNum * math.Pow(2, float64(lens-i-1))
}
diff --git a/utils/ext.go b/utils/ext.go
index 7c30fa3..1bf36c9 100644
--- a/utils/ext.go
+++ b/utils/ext.go
@@ -5,7 +5,7 @@ import (
"strings"
)
-func If(condition bool, trueValue, falseValue interface{}) interface{} {
+func If(condition bool, trueValue, falseValue any) any {
if condition {
return trueValue
}
@@ -21,8 +21,8 @@ func FirstToUpper(str string) string {
return strings.ToUpper(str[:1]) + strings.ToLower(str[1:])
}
-func ParseParams(in map[string]string) map[string]interface{} {
- out := make(map[string]interface{})
+func ParseParams(in map[string]string) map[string]any {
+ out := make(map[string]any)
for k, v := range in {
fv, err := strconv.ParseFloat(v, 64)
if err != nil {
diff --git a/utils/qrcode.go b/utils/qrcode.go
index faba89d..0103bde 100644
--- a/utils/qrcode.go
+++ b/utils/qrcode.go
@@ -1,457 +1,457 @@
-// Package utils 提供通用工具函数
-//
-// 二维码生成功能模块
-//
-// 本模块提供了完整的二维码生成功能,支持:
-// - 基础二维码生成(保存为PNG文件)
-// - 生成字节数组(可用于HTTP响应、数据库存储等)
-// - Base64编码输出(便于存储和传输)
-// - Data URL格式(可直接用于HTML
标签)
-// - 自定义配置(尺寸、颜色、纠错级别)
-// - 带Logo的二维码
-// - 批量生成二维码
-package utils
-
-import (
- "bytes"
- "encoding/base64"
- "fmt"
- "image"
- "image/color"
- "image/png"
- "os"
-
- "github.com/skip2/go-qrcode"
-)
-
-// QRCodeErrorLevel 二维码纠错级别
-//
-// 纠错级别决定了二维码能容忍多少损坏:
-// - 级别越高,容错能力越强,但二维码会更复杂
-// - 添加Logo时建议使用高级纠错
-// - 一般场景使用中级纠错即可
-type QRCodeErrorLevel int
-
-const (
- // QRCodeErrorLevelLow 低级纠错(约7%容错)
- // 适用场景:环境良好、追求小尺寸、内容较少
- QRCodeErrorLevelLow QRCodeErrorLevel = iota
-
- // QRCodeErrorLevelMedium 中级纠错(约15%容错)
- // 适用场景:通用场景(默认推荐)
- QRCodeErrorLevelMedium
-
- // QRCodeErrorLevelQuartile 较高级纠错(约25%容错)
- // 适用场景:需要较高容错能力
- QRCodeErrorLevelQuartile
-
- // QRCodeErrorLevelHigh 高级纠错(约30%容错)
- // 适用场景:添加Logo、容易损坏的环境、长期使用
- QRCodeErrorLevelHigh
-)
-
-const (
- // DefaultQRCodeSize 默认二维码尺寸(像素)
- DefaultQRCodeSize = 256
- // MinQRCodeSize 最小二维码尺寸
- MinQRCodeSize = 21
- // MaxQRCodeSize 最大二维码尺寸
- MaxQRCodeSize = 8192
-)
-
-// QRCodeConfig 二维码配置结构
-//
-// 用于自定义二维码的外观和质量参数
-//
-// 示例:
-//
-// config := &QRCodeConfig{
-// Size: 512, // 尺寸512x512像素
-// ErrorLevel: QRCodeErrorLevelHigh, // 高级纠错
-// ForegroundColor: color.RGBA{255, 0, 0, 255}, // 红色二维码
-// BackgroundColor: color.White, // 白色背景
-// }
-type QRCodeConfig struct {
- Size int // 尺寸(像素),范围:21-8192
- ErrorLevel QRCodeErrorLevel // 纠错级别,影响容错能力和复杂度
- ForegroundColor color.Color // 前景色(二维码颜色),建议使用深色
- BackgroundColor color.Color // 背景色,建议使用浅色以保证对比度
-}
-
-// DefaultQRCodeConfig 返回默认配置
-//
-// 默认配置适用于大多数场景:
-// - 尺寸:256x256像素(适合手机扫描)
-// - 纠错级别:中级(15%容错)
-// - 颜色:黑白配色(最佳识别率)
-//
-// 返回值:
-//
-// *QRCodeConfig: 默认配置对象
-func DefaultQRCodeConfig() *QRCodeConfig {
- return &QRCodeConfig{
- Size: DefaultQRCodeSize,
- ErrorLevel: QRCodeErrorLevelMedium,
- ForegroundColor: color.Black,
- BackgroundColor: color.White,
- }
-}
-
-// convertErrorLevel 转换纠错级别为底层库的纠错级别
-//
-// 将自定义的纠错级别枚举转换为 go-qrcode 库所需的格式
-//
-// 参数:
-//
-// level: 自定义纠错级别
-//
-// 返回值:
-//
-// qrcode.RecoveryLevel: go-qrcode库的纠错级别
-func convertErrorLevel(level QRCodeErrorLevel) qrcode.RecoveryLevel {
- switch level {
- case QRCodeErrorLevelLow:
- return qrcode.Low
- case QRCodeErrorLevelMedium:
- return qrcode.Medium
- case QRCodeErrorLevelQuartile:
- return qrcode.High
- case QRCodeErrorLevelHigh:
- return qrcode.Highest
- default:
- return qrcode.Medium
- }
-}
-
-// GenerateQRCode 生成二维码并保存为PNG文件
-//
-// 这是最简单的二维码生成方法,适合快速生成标准黑白二维码。
-// 使用中级纠错,黑白配色,PNG格式输出。
-//
-// 参数:
-//
-// content: 二维码内容(支持URL、文本、vCard、WiFi等格式)
-// filename: 保存的文件路径(.png文件)
-// size: 二维码尺寸(可选,单位:像素)
-// - 不传参数:使用默认256x256
-// - 传一个参数:使用指定尺寸
-// - 有效范围:21-8192像素
-//
-// 返回值:
-//
-// error: 错误信息,成功时返回nil
-//
-// 注意事项:
-// - 内容越长,二维码越复杂,建议尺寸>=256
-// - 文件权限为0644
-// - 会覆盖已存在的同名文件
-func GenerateQRCode(content, filename string, size ...int) error {
- qrSize := DefaultQRCodeSize
- if len(size) > 0 {
- qrSize = size[0]
- if qrSize < MinQRCodeSize || qrSize > MaxQRCodeSize {
- return fmt.Errorf("二维码尺寸必须在 %d 到 %d 之间", MinQRCodeSize, MaxQRCodeSize)
- }
- }
-
- err := qrcode.WriteFile(content, qrcode.Medium, qrSize, filename)
- if err != nil {
- return fmt.Errorf("生成二维码失败: %w", err)
- }
-
- return nil
-}
-
-// GenerateQRCodeBytes 生成二维码字节数组(PNG格式)
-//
-// 生成二维码的字节数组而不保存到文件,适合:
-// - HTTP响应直接返回图片
-// - 存储到数据库
-// - 通过网络传输
-// - 进一步处理(如添加到PDF)
-//
-// 参数:
-//
-// content: 二维码内容
-// size: 二维码尺寸(可选,默认256)
-//
-// 返回值:
-//
-// []byte: PNG格式的图片字节数组
-// error: 错误信息
-func GenerateQRCodeBytes(content string, size ...int) ([]byte, error) {
- qrSize := DefaultQRCodeSize
- if len(size) > 0 {
- qrSize = size[0]
- if qrSize < MinQRCodeSize || qrSize > MaxQRCodeSize {
- return nil, fmt.Errorf("二维码尺寸必须在 %d 到 %d 之间", MinQRCodeSize, MaxQRCodeSize)
- }
- }
-
- bytes, err := qrcode.Encode(content, qrcode.Medium, qrSize)
- if err != nil {
- return nil, fmt.Errorf("生成二维码失败: %w", err)
- }
-
- return bytes, nil
-}
-
-// GenerateQRCodeBase64 生成Base64编码的二维码字符串
-//
-// 将二维码图片编码为Base64字符串,便于:
-// - 存储到数据库的文本字段
-// - 通过JSON/XML传输
-// - 避免二进制数据处理问题
-//
-// 参数:
-//
-// content: 二维码内容
-// size: 二维码尺寸(可选,默认256)
-//
-// 返回值:
-//
-// string: Base64编码的字符串(不包含data:image/png;base64,前缀)
-// error: 错误信息
-func GenerateQRCodeBase64(content string, size ...int) (string, error) {
- qrBytes, err := GenerateQRCodeBytes(content, size...)
- if err != nil {
- return "", err
- }
-
- base64Str := base64.StdEncoding.EncodeToString(qrBytes)
- return base64Str, nil
-}
-
-// GenerateQRCodeWithConfig 使用自定义配置生成二维码
-//
-// 提供完全自定义的二维码生成能力,可以控制:
-// - 尺寸大小
-// - 纠错级别
-// - 前景色和背景色
-//
-// 参数:
-//
-// content: 二维码内容
-// config: 二维码配置对象(nil时使用默认配置)
-//
-// 返回值:
-//
-// []byte: PNG格式的字节数组
-// error: 错误信息
-//
-// 注意事项:
-// - 确保前景色和背景色有足够对比度
-// - 浅色前景配深色背景可能影响扫描
-func GenerateQRCodeWithConfig(content string, config *QRCodeConfig) ([]byte, error) {
- if config == nil {
- config = DefaultQRCodeConfig()
- }
-
- // 验证尺寸
- if config.Size < MinQRCodeSize || config.Size > MaxQRCodeSize {
- return nil, fmt.Errorf("二维码尺寸必须在 %d 到 %d 之间", MinQRCodeSize, MaxQRCodeSize)
- }
-
- // 创建二维码对象
- qr, err := qrcode.New(content, convertErrorLevel(config.ErrorLevel))
- if err != nil {
- return nil, fmt.Errorf("创建二维码失败: %w", err)
- }
-
- // 设置颜色
- qr.ForegroundColor = config.ForegroundColor
- qr.BackgroundColor = config.BackgroundColor
-
- // 生成PNG
- pngBytes, err := qr.PNG(config.Size)
- if err != nil {
- return nil, fmt.Errorf("生成PNG失败: %w", err)
- }
-
- return pngBytes, nil
-}
-
-// GenerateQRCodeWithLogo 生成带Logo的二维码
-//
-// 在二维码中心嵌入Logo图片,Logo会占据二维码约1/5的区域。
-// 使用高级纠错以确保Logo不影响二维码的可读性。
-//
-// 参数:
-//
-// content: 二维码内容
-// logoPath: Logo图片文件路径(支持PNG、JPEG等格式)
-// size: 二维码尺寸(可选,默认256,建议>=512以保证清晰度)
-//
-// 返回值:
-//
-// []byte: PNG格式的字节数组
-// error: 错误信息
-//
-// 注意事项:
-// - Logo会自动缩放到二维码的1/5大小
-// - 建议Logo使用正方形图片
-// - 使用高级纠错级别(~30%容错)
-// - Logo会覆盖二维码中心区域
-// - 建议二维码尺寸>=512以保证Logo清晰
-// - Logo文件必须存在且可读取
-func GenerateQRCodeWithLogo(content, logoPath string, size ...int) ([]byte, error) {
- qrSize := DefaultQRCodeSize
- if len(size) > 0 {
- qrSize = size[0]
- if qrSize < MinQRCodeSize || qrSize > MaxQRCodeSize {
- return nil, fmt.Errorf("二维码尺寸必须在 %d 到 %d 之间", MinQRCodeSize, MaxQRCodeSize)
- }
- }
-
- // 生成基础二维码
- qr, err := qrcode.New(content, qrcode.High)
- if err != nil {
- return nil, fmt.Errorf("创建二维码失败: %w", err)
- }
-
- // 生成二维码图像
- qrImage := qr.Image(qrSize)
-
- // 读取Logo图片
- logoFile, err := os.Open(logoPath)
- if err != nil {
- return nil, fmt.Errorf("打开Logo文件失败: %w", err)
- }
- defer logoFile.Close()
-
- logoImage, _, err := image.Decode(logoFile)
- if err != nil {
- return nil, fmt.Errorf("解码Logo图片失败: %w", err)
- }
-
- // 计算Logo位置和大小(Logo占二维码的1/5)
- logoSize := qrSize / 5
- logoX := (qrSize - logoSize) / 2
- logoY := (qrSize - logoSize) / 2
-
- // 创建最终图像
- finalImage := image.NewRGBA(qrImage.Bounds())
-
- // 绘制二维码
- for y := 0; y < qrSize; y++ {
- for x := 0; x < qrSize; x++ {
- finalImage.Set(x, y, qrImage.At(x, y))
- }
- }
-
- // 绘制Logo
- logoOriginalBounds := logoImage.Bounds()
- for y := 0; y < logoSize; y++ {
- for x := 0; x < logoSize; x++ {
- // 计算原始Logo的对应像素
- origX := x * logoOriginalBounds.Dx() / logoSize
- origY := y * logoOriginalBounds.Dy() / logoSize
- finalImage.Set(logoX+x, logoY+y, logoImage.At(origX, origY))
- }
- }
-
- // 转换为PNG字节
- var buf bytes.Buffer
- if err := png.Encode(&buf, finalImage); err != nil {
- return nil, fmt.Errorf("编码PNG失败: %w", err)
- }
-
- return buf.Bytes(), nil
-}
-
-// SaveQRCodeBytes 保存二维码字节数组到文件
-//
-// 将二维码字节数组保存为PNG文件,常与 GenerateQRCodeBytes 或
-// GenerateQRCodeWithConfig 配合使用。
-//
-// 参数:
-//
-// data: PNG格式的二维码字节数组
-// filename: 保存的文件路径
-//
-// 返回值:
-//
-// error: 错误信息
-//
-// 注意事项:
-// - 文件权限为0644
-// - 会覆盖已存在的文件
-// - 确保目录已存在
-func SaveQRCodeBytes(data []byte, filename string) error {
- if err := os.WriteFile(filename, data, 0644); err != nil {
- return fmt.Errorf("保存文件失败: %w", err)
- }
- return nil
-}
-
-// GenerateQRCodeDataURL 生成Data URL格式的二维码
-//
-// 生成可以直接用于HTML
标签的Data URL格式字符串。
-// Data URL包含了完整的图片数据,无需额外的图片文件。
-//
-// 参数:
-//
-// content: 二维码内容
-// size: 二维码尺寸(可选,默认256)
-//
-// 返回值:
-//
-// string: Data URL格式字符串(包含data:image/png;base64,前缀)
-// error: 错误信息
-//
-// 注意事项:
-// - Data URL字符串较长,不适合存储到数据库
-// - 适合临时显示、前端渲染等场景
-// - 某些老旧浏览器可能不支持
-func GenerateQRCodeDataURL(content string, size ...int) (string, error) {
- qrBytes, err := GenerateQRCodeBytes(content, size...)
- if err != nil {
- return "", err
- }
-
- base64Str := base64.StdEncoding.EncodeToString(qrBytes)
- dataURL := fmt.Sprintf("data:image/png;base64,%s", base64Str)
- return dataURL, nil
-}
-
-// BatchGenerateQRCode 批量生成二维码文件
-//
-// 一次性生成多个二维码文件,适合:
-// - 批量生成产品二维码
-// - 生成多个用户的会员卡
-// - 批量生成门票、优惠券等
-//
-// 参数:
-//
-// items: map[内容]文件名,例如:map["产品A":"qr_a.png", "产品B":"qr_b.png"]
-// size: 二维码尺寸(可选,默认256,所有二维码使用相同尺寸)
-//
-// 返回值:
-//
-// []string: 失败的文件名列表(成功时为nil)
-// error: 错误信息(部分失败时返回错误,但成功的文件已生成)
-//
-// 注意事项:
-// - 即使部分失败,成功的二维码仍会生成
-// - 建议检查返回的failed列表以确定哪些失败了
-// - 大批量生成时注意磁盘空间
-func BatchGenerateQRCode(items map[string]string, size ...int) ([]string, error) {
- var failed []string
- qrSize := DefaultQRCodeSize
- if len(size) > 0 {
- qrSize = size[0]
- }
-
- for content, filename := range items {
- err := GenerateQRCode(content, filename, qrSize)
- if err != nil {
- failed = append(failed, filename)
- }
- }
-
- if len(failed) > 0 {
- return failed, fmt.Errorf("有 %d 个二维码生成失败", len(failed))
- }
-
- return nil, nil
-}
+// Package utils 提供通用工具函数
+//
+// 二维码生成功能模块
+//
+// 本模块提供了完整的二维码生成功能,支持:
+// - 基础二维码生成(保存为PNG文件)
+// - 生成字节数组(可用于HTTP响应、数据库存储等)
+// - Base64编码输出(便于存储和传输)
+// - Data URL格式(可直接用于HTML
标签)
+// - 自定义配置(尺寸、颜色、纠错级别)
+// - 带Logo的二维码
+// - 批量生成二维码
+package utils
+
+import (
+ "bytes"
+ "encoding/base64"
+ "fmt"
+ "image"
+ "image/color"
+ "image/png"
+ "os"
+
+ "github.com/skip2/go-qrcode"
+)
+
+// QRCodeErrorLevel 二维码纠错级别
+//
+// 纠错级别决定了二维码能容忍多少损坏:
+// - 级别越高,容错能力越强,但二维码会更复杂
+// - 添加Logo时建议使用高级纠错
+// - 一般场景使用中级纠错即可
+type QRCodeErrorLevel int
+
+const (
+ // QRCodeErrorLevelLow 低级纠错(约7%容错)
+ // 适用场景:环境良好、追求小尺寸、内容较少
+ QRCodeErrorLevelLow QRCodeErrorLevel = iota
+
+ // QRCodeErrorLevelMedium 中级纠错(约15%容错)
+ // 适用场景:通用场景(默认推荐)
+ QRCodeErrorLevelMedium
+
+ // QRCodeErrorLevelQuartile 较高级纠错(约25%容错)
+ // 适用场景:需要较高容错能力
+ QRCodeErrorLevelQuartile
+
+ // QRCodeErrorLevelHigh 高级纠错(约30%容错)
+ // 适用场景:添加Logo、容易损坏的环境、长期使用
+ QRCodeErrorLevelHigh
+)
+
+const (
+ // DefaultQRCodeSize 默认二维码尺寸(像素)
+ DefaultQRCodeSize = 256
+ // MinQRCodeSize 最小二维码尺寸
+ MinQRCodeSize = 21
+ // MaxQRCodeSize 最大二维码尺寸
+ MaxQRCodeSize = 8192
+)
+
+// QRCodeConfig 二维码配置结构
+//
+// 用于自定义二维码的外观和质量参数
+//
+// 示例:
+//
+// config := &QRCodeConfig{
+// Size: 512, // 尺寸512x512像素
+// ErrorLevel: QRCodeErrorLevelHigh, // 高级纠错
+// ForegroundColor: color.RGBA{255, 0, 0, 255}, // 红色二维码
+// BackgroundColor: color.White, // 白色背景
+// }
+type QRCodeConfig struct {
+ Size int // 尺寸(像素),范围:21-8192
+ ErrorLevel QRCodeErrorLevel // 纠错级别,影响容错能力和复杂度
+ ForegroundColor color.Color // 前景色(二维码颜色),建议使用深色
+ BackgroundColor color.Color // 背景色,建议使用浅色以保证对比度
+}
+
+// DefaultQRCodeConfig 返回默认配置
+//
+// 默认配置适用于大多数场景:
+// - 尺寸:256x256像素(适合手机扫描)
+// - 纠错级别:中级(15%容错)
+// - 颜色:黑白配色(最佳识别率)
+//
+// 返回值:
+//
+// *QRCodeConfig: 默认配置对象
+func DefaultQRCodeConfig() *QRCodeConfig {
+ return &QRCodeConfig{
+ Size: DefaultQRCodeSize,
+ ErrorLevel: QRCodeErrorLevelMedium,
+ ForegroundColor: color.Black,
+ BackgroundColor: color.White,
+ }
+}
+
+// convertErrorLevel 转换纠错级别为底层库的纠错级别
+//
+// 将自定义的纠错级别枚举转换为 go-qrcode 库所需的格式
+//
+// 参数:
+//
+// level: 自定义纠错级别
+//
+// 返回值:
+//
+// qrcode.RecoveryLevel: go-qrcode库的纠错级别
+func convertErrorLevel(level QRCodeErrorLevel) qrcode.RecoveryLevel {
+ switch level {
+ case QRCodeErrorLevelLow:
+ return qrcode.Low
+ case QRCodeErrorLevelMedium:
+ return qrcode.Medium
+ case QRCodeErrorLevelQuartile:
+ return qrcode.High
+ case QRCodeErrorLevelHigh:
+ return qrcode.Highest
+ default:
+ return qrcode.Medium
+ }
+}
+
+// GenerateQRCode 生成二维码并保存为PNG文件
+//
+// 这是最简单的二维码生成方法,适合快速生成标准黑白二维码。
+// 使用中级纠错,黑白配色,PNG格式输出。
+//
+// 参数:
+//
+// content: 二维码内容(支持URL、文本、vCard、WiFi等格式)
+// filename: 保存的文件路径(.png文件)
+// size: 二维码尺寸(可选,单位:像素)
+// - 不传参数:使用默认256x256
+// - 传一个参数:使用指定尺寸
+// - 有效范围:21-8192像素
+//
+// 返回值:
+//
+// error: 错误信息,成功时返回nil
+//
+// 注意事项:
+// - 内容越长,二维码越复杂,建议尺寸>=256
+// - 文件权限为0644
+// - 会覆盖已存在的同名文件
+func GenerateQRCode(content, filename string, size ...int) error {
+ qrSize := DefaultQRCodeSize
+ if len(size) > 0 {
+ qrSize = size[0]
+ if qrSize < MinQRCodeSize || qrSize > MaxQRCodeSize {
+ return fmt.Errorf("二维码尺寸必须在 %d 到 %d 之间", MinQRCodeSize, MaxQRCodeSize)
+ }
+ }
+
+ err := qrcode.WriteFile(content, qrcode.Medium, qrSize, filename)
+ if err != nil {
+ return fmt.Errorf("生成二维码失败: %w", err)
+ }
+
+ return nil
+}
+
+// GenerateQRCodeBytes 生成二维码字节数组(PNG格式)
+//
+// 生成二维码的字节数组而不保存到文件,适合:
+// - HTTP响应直接返回图片
+// - 存储到数据库
+// - 通过网络传输
+// - 进一步处理(如添加到PDF)
+//
+// 参数:
+//
+// content: 二维码内容
+// size: 二维码尺寸(可选,默认256)
+//
+// 返回值:
+//
+// []byte: PNG格式的图片字节数组
+// error: 错误信息
+func GenerateQRCodeBytes(content string, size ...int) ([]byte, error) {
+ qrSize := DefaultQRCodeSize
+ if len(size) > 0 {
+ qrSize = size[0]
+ if qrSize < MinQRCodeSize || qrSize > MaxQRCodeSize {
+ return nil, fmt.Errorf("二维码尺寸必须在 %d 到 %d 之间", MinQRCodeSize, MaxQRCodeSize)
+ }
+ }
+
+ bytes, err := qrcode.Encode(content, qrcode.Medium, qrSize)
+ if err != nil {
+ return nil, fmt.Errorf("生成二维码失败: %w", err)
+ }
+
+ return bytes, nil
+}
+
+// GenerateQRCodeBase64 生成Base64编码的二维码字符串
+//
+// 将二维码图片编码为Base64字符串,便于:
+// - 存储到数据库的文本字段
+// - 通过JSON/XML传输
+// - 避免二进制数据处理问题
+//
+// 参数:
+//
+// content: 二维码内容
+// size: 二维码尺寸(可选,默认256)
+//
+// 返回值:
+//
+// string: Base64编码的字符串(不包含data:image/png;base64,前缀)
+// error: 错误信息
+func GenerateQRCodeBase64(content string, size ...int) (string, error) {
+ qrBytes, err := GenerateQRCodeBytes(content, size...)
+ if err != nil {
+ return "", err
+ }
+
+ base64Str := base64.StdEncoding.EncodeToString(qrBytes)
+ return base64Str, nil
+}
+
+// GenerateQRCodeWithConfig 使用自定义配置生成二维码
+//
+// 提供完全自定义的二维码生成能力,可以控制:
+// - 尺寸大小
+// - 纠错级别
+// - 前景色和背景色
+//
+// 参数:
+//
+// content: 二维码内容
+// config: 二维码配置对象(nil时使用默认配置)
+//
+// 返回值:
+//
+// []byte: PNG格式的字节数组
+// error: 错误信息
+//
+// 注意事项:
+// - 确保前景色和背景色有足够对比度
+// - 浅色前景配深色背景可能影响扫描
+func GenerateQRCodeWithConfig(content string, config *QRCodeConfig) ([]byte, error) {
+ if config == nil {
+ config = DefaultQRCodeConfig()
+ }
+
+ // 验证尺寸
+ if config.Size < MinQRCodeSize || config.Size > MaxQRCodeSize {
+ return nil, fmt.Errorf("二维码尺寸必须在 %d 到 %d 之间", MinQRCodeSize, MaxQRCodeSize)
+ }
+
+ // 创建二维码对象
+ qr, err := qrcode.New(content, convertErrorLevel(config.ErrorLevel))
+ if err != nil {
+ return nil, fmt.Errorf("创建二维码失败: %w", err)
+ }
+
+ // 设置颜色
+ qr.ForegroundColor = config.ForegroundColor
+ qr.BackgroundColor = config.BackgroundColor
+
+ // 生成PNG
+ pngBytes, err := qr.PNG(config.Size)
+ if err != nil {
+ return nil, fmt.Errorf("生成PNG失败: %w", err)
+ }
+
+ return pngBytes, nil
+}
+
+// GenerateQRCodeWithLogo 生成带Logo的二维码
+//
+// 在二维码中心嵌入Logo图片,Logo会占据二维码约1/5的区域。
+// 使用高级纠错以确保Logo不影响二维码的可读性。
+//
+// 参数:
+//
+// content: 二维码内容
+// logoPath: Logo图片文件路径(支持PNG、JPEG等格式)
+// size: 二维码尺寸(可选,默认256,建议>=512以保证清晰度)
+//
+// 返回值:
+//
+// []byte: PNG格式的字节数组
+// error: 错误信息
+//
+// 注意事项:
+// - Logo会自动缩放到二维码的1/5大小
+// - 建议Logo使用正方形图片
+// - 使用高级纠错级别(~30%容错)
+// - Logo会覆盖二维码中心区域
+// - 建议二维码尺寸>=512以保证Logo清晰
+// - Logo文件必须存在且可读取
+func GenerateQRCodeWithLogo(content, logoPath string, size ...int) ([]byte, error) {
+ qrSize := DefaultQRCodeSize
+ if len(size) > 0 {
+ qrSize = size[0]
+ if qrSize < MinQRCodeSize || qrSize > MaxQRCodeSize {
+ return nil, fmt.Errorf("二维码尺寸必须在 %d 到 %d 之间", MinQRCodeSize, MaxQRCodeSize)
+ }
+ }
+
+ // 生成基础二维码
+ qr, err := qrcode.New(content, qrcode.High)
+ if err != nil {
+ return nil, fmt.Errorf("创建二维码失败: %w", err)
+ }
+
+ // 生成二维码图像
+ qrImage := qr.Image(qrSize)
+
+ // 读取Logo图片
+ logoFile, err := os.Open(logoPath)
+ if err != nil {
+ return nil, fmt.Errorf("打开Logo文件失败: %w", err)
+ }
+ defer logoFile.Close()
+
+ logoImage, _, err := image.Decode(logoFile)
+ if err != nil {
+ return nil, fmt.Errorf("解码Logo图片失败: %w", err)
+ }
+
+ // 计算Logo位置和大小(Logo占二维码的1/5)
+ logoSize := qrSize / 5
+ logoX := (qrSize - logoSize) / 2
+ logoY := (qrSize - logoSize) / 2
+
+ // 创建最终图像
+ finalImage := image.NewRGBA(qrImage.Bounds())
+
+ // 绘制二维码
+ for y := 0; y < qrSize; y++ {
+ for x := 0; x < qrSize; x++ {
+ finalImage.Set(x, y, qrImage.At(x, y))
+ }
+ }
+
+ // 绘制Logo
+ logoOriginalBounds := logoImage.Bounds()
+ for y := range logoSize {
+ for x := range logoSize {
+ // 计算原始Logo的对应像素
+ origX := x * logoOriginalBounds.Dx() / logoSize
+ origY := y * logoOriginalBounds.Dy() / logoSize
+ finalImage.Set(logoX+x, logoY+y, logoImage.At(origX, origY))
+ }
+ }
+
+ // 转换为PNG字节
+ var buf bytes.Buffer
+ if err := png.Encode(&buf, finalImage); err != nil {
+ return nil, fmt.Errorf("编码PNG失败: %w", err)
+ }
+
+ return buf.Bytes(), nil
+}
+
+// SaveQRCodeBytes 保存二维码字节数组到文件
+//
+// 将二维码字节数组保存为PNG文件,常与 GenerateQRCodeBytes 或
+// GenerateQRCodeWithConfig 配合使用。
+//
+// 参数:
+//
+// data: PNG格式的二维码字节数组
+// filename: 保存的文件路径
+//
+// 返回值:
+//
+// error: 错误信息
+//
+// 注意事项:
+// - 文件权限为0644
+// - 会覆盖已存在的文件
+// - 确保目录已存在
+func SaveQRCodeBytes(data []byte, filename string) error {
+ if err := os.WriteFile(filename, data, 0644); err != nil {
+ return fmt.Errorf("保存文件失败: %w", err)
+ }
+ return nil
+}
+
+// GenerateQRCodeDataURL 生成Data URL格式的二维码
+//
+// 生成可以直接用于HTML
标签的Data URL格式字符串。
+// Data URL包含了完整的图片数据,无需额外的图片文件。
+//
+// 参数:
+//
+// content: 二维码内容
+// size: 二维码尺寸(可选,默认256)
+//
+// 返回值:
+//
+// string: Data URL格式字符串(包含data:image/png;base64,前缀)
+// error: 错误信息
+//
+// 注意事项:
+// - Data URL字符串较长,不适合存储到数据库
+// - 适合临时显示、前端渲染等场景
+// - 某些老旧浏览器可能不支持
+func GenerateQRCodeDataURL(content string, size ...int) (string, error) {
+ qrBytes, err := GenerateQRCodeBytes(content, size...)
+ if err != nil {
+ return "", err
+ }
+
+ base64Str := base64.StdEncoding.EncodeToString(qrBytes)
+ dataURL := fmt.Sprintf("data:image/png;base64,%s", base64Str)
+ return dataURL, nil
+}
+
+// BatchGenerateQRCode 批量生成二维码文件
+//
+// 一次性生成多个二维码文件,适合:
+// - 批量生成产品二维码
+// - 生成多个用户的会员卡
+// - 批量生成门票、优惠券等
+//
+// 参数:
+//
+// items: map[内容]文件名,例如:map["产品A":"qr_a.png", "产品B":"qr_b.png"]
+// size: 二维码尺寸(可选,默认256,所有二维码使用相同尺寸)
+//
+// 返回值:
+//
+// []string: 失败的文件名列表(成功时为nil)
+// error: 错误信息(部分失败时返回错误,但成功的文件已生成)
+//
+// 注意事项:
+// - 即使部分失败,成功的二维码仍会生成
+// - 建议检查返回的failed列表以确定哪些失败了
+// - 大批量生成时注意磁盘空间
+func BatchGenerateQRCode(items map[string]string, size ...int) ([]string, error) {
+ var failed []string
+ qrSize := DefaultQRCodeSize
+ if len(size) > 0 {
+ qrSize = size[0]
+ }
+
+ for content, filename := range items {
+ err := GenerateQRCode(content, filename, qrSize)
+ if err != nil {
+ failed = append(failed, filename)
+ }
+ }
+
+ if len(failed) > 0 {
+ return failed, fmt.Errorf("有 %d 个二维码生成失败", len(failed))
+ }
+
+ return nil, nil
+}
diff --git a/utils/random.go b/utils/random.go
index 176bb2b..b72c82b 100644
--- a/utils/random.go
+++ b/utils/random.go
@@ -9,7 +9,7 @@ func RandomString(l int) string {
str := "0123456789AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz"
bytes := []byte(str)
var result []byte = make([]byte, 0, l)
- for i := 0; i < l; i++ {
+ for range l {
result = append(result, bytes[rand.IntN(len(bytes))])
}
return string(result)
@@ -20,7 +20,7 @@ func RandomPureString(l int) string {
str := "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz"
bytes := []byte(str)
var result []byte = make([]byte, 0, l)
- for i := 0; i < l; i++ {
+ for range l {
result = append(result, bytes[rand.IntN(len(bytes))])
}
return string(result)
@@ -31,7 +31,7 @@ func RandomPureUpString(l int) string {
str := "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
bytes := []byte(str)
var result []byte = make([]byte, 0, l)
- for i := 0; i < l; i++ {
+ for range l {
result = append(result, bytes[rand.IntN(len(bytes))])
}
return string(result)
@@ -42,7 +42,7 @@ func RandomNumber(l int) string {
str := "0123456789"
bytes := []byte(str)
var result []byte
- for i := 0; i < l; i++ {
+ for range l {
result = append(result, bytes[rand.IntN(len(bytes))])
}
return string(result)