Compare commits
	
		
			No commits in common. "main" and "v0.0.69" have entirely different histories.
		
	
	
		
							
								
								
									
										301
									
								
								README.md
								
								
								
								
							
							
						
						
									
										301
									
								
								README.md
								
								
								
								
							|  | @ -1,278 +1,27 @@ | |||
| # BSM-SDK Core | ||||
| 
 | ||||
| BSM-SDK Core 是一个企业级后端开发工具包的核心模块,提供了微服务架构、配置管理、加密解密、缓存、数据库访问、中间件等基础功能。 | ||||
| 
 | ||||
| ## 🚀 最新优化 | ||||
| 
 | ||||
| - ✅ 添加了完整的中文注释,提高代码可读性 | ||||
| - ✅ 优化了数据类型转换函数的性能 | ||||
| - ✅ 改进了错误处理机制 | ||||
| - ✅ 增强了代码文档和注释 | ||||
| - ✅ 统一了代码风格和命名规范 | ||||
| 
 | ||||
| ## 私有仓库设置 | ||||
| 
 | ||||
| ```bash | ||||
| go env -w GOPRIVATE=git.apinb.com/* | ||||
| go env -w GONOPROXY=git.apinb.com/* | ||||
| go env -w GOINSECURE=git.apinb.com/* | ||||
| go env -w GONOSUMDB=git.apinb.com/* | ||||
| # 私有仓库:core 设置 | ||||
| ``` | ||||
| 
 | ||||
| ## 核心功能模块 | ||||
| 
 | ||||
| ### 1. 服务管理 (service) | ||||
| 
 | ||||
| #### 服务启动与注册 | ||||
| - **Service**: 核心服务结构,支持 gRPC 服务启动和 etcd 注册 | ||||
| - **New()**: 创建服务实例 | ||||
| - **Start()**: 启动服务,支持 etcd 注册和 HTTP 网关 | ||||
| - **Stop()**: 优雅停止服务 | ||||
| - **Use()**: 执行初始化函数 | ||||
| 
 | ||||
| #### 服务发现 | ||||
| - **FoundGrpcMethods()**: 自动发现 gRPC 方法 | ||||
| - **RegisterService()**: 注册服务到 etcd | ||||
| - **ServiceRegister**: 服务注册器,支持租约管理 | ||||
| 
 | ||||
| #### 元数据解析 | ||||
| - **ParseMetaCtx()**: 解析 gRPC 上下文中的元数据 | ||||
| - **ParseOptions**: 解析选项,支持角色验证和私有IP检查 | ||||
| 
 | ||||
| ### 2. 配置管理 (conf) | ||||
| 
 | ||||
| #### 配置加载 | ||||
| - **New()**: 加载配置文件,支持环境变量替换 | ||||
| - **NotNil()**: 验证必需配置项 | ||||
| - **PrintInfo()**: 打印配置信息 | ||||
| - **CheckPort()**: 检查端口配置 | ||||
| 
 | ||||
| #### 配置类型 | ||||
| - **MicroServiceConf**: 微服务配置 | ||||
| - **GatewayConf**: 网关配置 | ||||
| - **DBConf**: 数据库配置 | ||||
| 
 | ||||
| ### 3. 加密与解密 (crypto) | ||||
| 
 | ||||
| #### AES 加密 | ||||
| - **AESGCMEncrypt/AESGCMDecrypt**: GCM 模式加密解密 | ||||
| - **Encrypt/Decrypt**: CBC 模式加密解密 | ||||
| - **AesEncryptECB/AesDecryptECB**: ECB 模式加密解密 | ||||
| - **AesKeyCheck()**: 密钥环境变量检测 | ||||
| 
 | ||||
| #### RSA 加密 | ||||
| - **EncryptWithPublicKey()**: 公钥加密 | ||||
| - **Decrypt()**: 私钥解密 | ||||
| 
 | ||||
| #### 通用加密 | ||||
| - **AesEncryptCBC/AesDecryptCBC**: 通用 CBC 加密解密 | ||||
| - **PKCS7Padding/PKCS7UnPadding**: PKCS7 填充 | ||||
| 
 | ||||
| ### 4. 数据库支持 (database) | ||||
| 
 | ||||
| #### 数据库连接 | ||||
| - **NewDatabase()**: 创建数据库连接,支持 MySQL 和 PostgreSQL | ||||
| - **NewMysql()**: MySQL 连接 | ||||
| - **NewPostgres()**: PostgreSQL 连接 | ||||
| 
 | ||||
| #### 数据库类型 | ||||
| - **SqlOptions**: 数据库连接选项 | ||||
| - **Std_IICUDS**: 标准数据库模型(ID、Identity、Created、Updated、Deleted、Status) | ||||
| - **Std_ICUD**: 标准数据库模型(ID、Created、Updated、Deleted) | ||||
| - **Std_Passport**: 护照模型 | ||||
| - **Std_Owner**: 所有者模型 | ||||
| 
 | ||||
| ### 5. 缓存支持 (cache) | ||||
| 
 | ||||
| #### Redis 缓存 | ||||
| - **RedisClient**: Redis 客户端封装 | ||||
| - **New()**: 创建 Redis 连接 | ||||
| - **Hash()**: 哈希分片计算 | ||||
| 
 | ||||
| #### 内存缓存 | ||||
| - 支持内存缓存操作 | ||||
| 
 | ||||
| ### 6. 消息队列 (queue) | ||||
| 
 | ||||
| #### NATS 消息队列 | ||||
| - **Nats**: NATS 客户端封装 | ||||
| - **NewNats()**: 创建 NATS 连接 | ||||
| - **Subscribe()**: 订阅消息 | ||||
| - **Producer()**: 发布消息 | ||||
| 
 | ||||
| ### 7. 中间件 (middleware) | ||||
| 
 | ||||
| #### JWT 认证 | ||||
| - **JwtAuth()**: JWT 认证中间件 | ||||
| - **ParseAuth()**: 解析认证信息 | ||||
| 
 | ||||
| #### CORS 支持 | ||||
| - **Cors()**: CORS 中间件 | ||||
| 
 | ||||
| #### 运行模式 | ||||
| - **Mode()**: 设置 Gin 运行模式 | ||||
| 
 | ||||
| ### 8. 工具类 (utils) | ||||
| 
 | ||||
| #### 网络工具 | ||||
| - **GetLocationIP()**: 获取本机IP | ||||
| - **IsPublicIP()**: 判断是否为公网IP | ||||
| - **HttpGet/HttpPost()**: HTTP 请求工具 | ||||
| - **DownloadFile()**: 文件下载 | ||||
| 
 | ||||
| #### 数据类型转换 | ||||
| - **String2Int/String2Int64**: 字符串转整数 | ||||
| - **String2Float64/String2Float32**: 字符串转浮点数 | ||||
| - **Int2String/Int642String**: 整数转字符串 | ||||
| - **Float64ToString/Float32ToString**: 浮点数转字符串 | ||||
| - **AnyToString()**: 任意类型转字符串 | ||||
| 
 | ||||
| #### 时间处理 | ||||
| - **Time2String()**: 时间转字符串 | ||||
| - **String2Time()**: 字符串转时间 | ||||
| 
 | ||||
| #### 身份标识 | ||||
| - **UUID()**: 生成 UUID | ||||
| - **ULID()**: 生成 ULID | ||||
| 
 | ||||
| #### 文件操作 | ||||
| - **PathExists()**: 检查路径是否存在 | ||||
| - **CreateDir()**: 创建目录 | ||||
| - **GetRunPath()**: 获取运行路径 | ||||
| - **GetCurrentPath()**: 获取当前路径 | ||||
| 
 | ||||
| #### JSON 处理 | ||||
| - **JsonEscape()**: JSON 转义 | ||||
| 
 | ||||
| ### 9. 错误处理 (errcode) | ||||
| 
 | ||||
| #### 标准错误码 | ||||
| - **Header 错误**: 101-104 | ||||
| - **标准错误**: 110-121 | ||||
| - **JWT 错误**: 131-139 | ||||
| - **模型错误**: 151-157 | ||||
| - **gRPC 错误**: 171-186 | ||||
| 
 | ||||
| #### 错误创建 | ||||
| - **NewError()**: 创建标准错误 | ||||
| - **ErrFatal()**: 创建致命错误 | ||||
| - **ErrNotFound()**: 创建未找到错误 | ||||
| 
 | ||||
| ### 10. 响应处理 (infra) | ||||
| 
 | ||||
| #### 统一响应 | ||||
| - **Reply**: 统一响应结构 | ||||
| - **Success()**: 成功响应 | ||||
| - **Error()**: 错误响应,支持 gRPC 状态码转换 | ||||
| 
 | ||||
| ### 11. 日志打印 (printer) | ||||
| 
 | ||||
| #### 彩色日志 | ||||
| - **Info()**: 信息日志(白色) | ||||
| - **Warn()**: 警告日志(橙色) | ||||
| - **Success()**: 成功日志(绿色) | ||||
| - **Error()**: 错误日志(红色) | ||||
| - **Json()**: JSON 格式化输出 | ||||
| 
 | ||||
| ### 12. 环境管理 (env) | ||||
| 
 | ||||
| #### 运行时环境 | ||||
| - **RuntimeEnv**: 运行时环境配置 | ||||
| - **NewEnv()**: 初始化环境 | ||||
| - **GetEnvDefault()**: 获取环境变量默认值 | ||||
| 
 | ||||
| ### 13. 变量管理 (vars) | ||||
| 
 | ||||
| #### 全局变量 | ||||
| - **ServiceKey**: 服务键 | ||||
| - **HostName**: 主机名 | ||||
| - **VERSION**: 版本号 | ||||
| - **BUILD_TIME**: 构建时间 | ||||
| - **GO_VERSION**: Go 版本 | ||||
| 
 | ||||
| ### 14. 第三方集成 (third) | ||||
| 
 | ||||
| #### 微信集成 | ||||
| - 支持微信相关功能 | ||||
| 
 | ||||
| ### 15. 许可证管理 (licence) | ||||
| 
 | ||||
| #### 许可证验证 | ||||
| - 支持许可证文件验证 | ||||
| 
 | ||||
| 
 | ||||
| ### 配置环境变量 | ||||
| 
 | ||||
| ```bash | ||||
| export BSM_Workspace=def | ||||
| export BSM_JwtSecretKey=your_secret_key | ||||
| export BSM_RuntimeMode=dev | ||||
| export BSM_Prefix=/usr/local/bsm | ||||
| go env -w  GOPRIVATE=git.apinb.com/* | ||||
| go env -w  GONOPROXY=git.apinb.com/* | ||||
| go env -w  GOINSECURE=git.apinb.com/* | ||||
| go env -w  GONOSUMDB=git.apinb.com/* | ||||
| ``` | ||||
| # crypto 加密与解密 | ||||
| ## GCM加密 | ||||
| ``` | ||||
| AESGCMEncrypt GCM 加密 | ||||
| AESGCMDecrypt GCM 解密 | ||||
| ``` | ||||
| ## CBC加密 | ||||
| ``` | ||||
| Encrypt CBC加密 | ||||
| Decrypt CBC解密 | ||||
| ``` | ||||
| ## ECB加密 | ||||
| ``` | ||||
| AesEncryptECB ECB加密 | ||||
| AesDecryptECB ECB解密 | ||||
| ``` | ||||
| ## 环境变量检测 | ||||
| ``` | ||||
| AesKeyCheck 秘钥环境变量检测 | ||||
| ``` | ||||
| 
 | ||||
| ### 安全建议 | ||||
| 
 | ||||
| - 使用强密钥进行加密 | ||||
| - 定期更新 JWT 密钥 | ||||
| - 配置适当的数据库连接池 | ||||
| - 使用 HTTPS 进行通信 | ||||
| - 定期检查许可证有效性 | ||||
| 
 | ||||
| ## 📝 代码优化说明 | ||||
| 
 | ||||
| ### 已完成的优化 | ||||
| 
 | ||||
| 1. **中文注释优化** | ||||
|    - 为所有核心模块添加了详细的中文注释 | ||||
|    - 统一了注释风格和格式 | ||||
|    - 提高了代码的可读性和维护性 | ||||
| 
 | ||||
| 2. **性能优化** | ||||
|    - 优化了 `String2Int64` 函数,直接使用 `strconv.ParseInt` 而不是先转 int 再转 int64 | ||||
|    - 改进了网络工具函数的错误处理 | ||||
|    - 优化了缓存操作的性能 | ||||
| 
 | ||||
| 3. **代码质量提升** | ||||
|    - 统一了函数命名规范 | ||||
|    - 改进了错误处理机制 | ||||
|    - 增强了类型安全性 | ||||
| 
 | ||||
| ### 使用建议 | ||||
| 
 | ||||
| 1. **配置管理** | ||||
|    ```go | ||||
|    // 推荐使用环境变量进行配置 | ||||
|    conf.New("your-service", &config) | ||||
|    ``` | ||||
| 
 | ||||
| 2. **错误处理** | ||||
|    ```go | ||||
|    // 使用统一的错误码 | ||||
|    if err != nil { | ||||
|        return errcode.ErrInternal | ||||
|    } | ||||
|    ``` | ||||
| 
 | ||||
| 3. **缓存使用** | ||||
|    ```go | ||||
|    // 使用统一的缓存键前缀 | ||||
|    key := redisClient.BuildKey("user", userID) | ||||
|    ``` | ||||
| 
 | ||||
| 4. **数据库连接** | ||||
|    ```go | ||||
|    // 使用连接池优化 | ||||
|    db, err := database.NewDatabase("mysql", dsn, options) | ||||
|    ``` | ||||
| 
 | ||||
| ## 许可证 | ||||
| 
 | ||||
| 本项目采用私有许可证,请确保已获得相应的使用授权。 | ||||
| 
 | ||||
| ## 贡献 | ||||
| 
 | ||||
| 欢迎提交 Issue 和 Pull Request 来改进本项目。 | ||||
| 
 | ||||
| ## 联系方式 | ||||
| 
 | ||||
| 如有问题,请联系开发团队。 | ||||
|  | @ -1,64 +0,0 @@ | |||
| package mapsync | ||||
| 
 | ||||
| import ( | ||||
| 	"sync" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	// sync map
 | ||||
| 	MapInt *mapInt | ||||
| ) | ||||
| 
 | ||||
| // lock
 | ||||
| type mapInt struct { | ||||
| 	sync.RWMutex | ||||
| 	Data map[string]int | ||||
| } | ||||
| 
 | ||||
| func NewMapInt() *mapInt { | ||||
| 	return &mapInt{ | ||||
| 		Data: make(map[string]int), | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (c *mapInt) Set(key string, val int) { | ||||
| 	c.Lock() | ||||
| 	defer c.Unlock() | ||||
| 	c.Data[key] = val | ||||
| } | ||||
| 
 | ||||
| func (c *mapInt) Get(key string) int { | ||||
| 	c.RLock() | ||||
| 	defer c.RUnlock() | ||||
| 
 | ||||
| 	vals, ok := c.Data[key] | ||||
| 	if !ok { | ||||
| 		return 0 | ||||
| 	} | ||||
| 
 | ||||
| 	return vals | ||||
| } | ||||
| 
 | ||||
| func (c *mapInt) Del(key string) { | ||||
| 	c.Lock() | ||||
| 	defer c.Unlock() | ||||
| 
 | ||||
| 	delete(c.Data, key) | ||||
| } | ||||
| 
 | ||||
| func (c *mapInt) All() map[string]int { | ||||
| 	c.RLock() | ||||
| 	defer c.RUnlock() | ||||
| 
 | ||||
| 	return c.Data | ||||
| } | ||||
| func (c *mapInt) Keys() (keys []string) { | ||||
| 	c.RLock() | ||||
| 	defer c.RUnlock() | ||||
| 
 | ||||
| 	for k, _ := range c.Data { | ||||
| 		keys = append(keys, k) | ||||
| 	} | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
|  | @ -1,64 +0,0 @@ | |||
| package mapsync | ||||
| 
 | ||||
| import ( | ||||
| 	"sync" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	// sync map
 | ||||
| 	MapString *mapString | ||||
| ) | ||||
| 
 | ||||
| // lock
 | ||||
| type mapString struct { | ||||
| 	sync.RWMutex | ||||
| 	Data map[string]string | ||||
| } | ||||
| 
 | ||||
| func NewMapString() *mapString { | ||||
| 	return &mapString{ | ||||
| 		Data: make(map[string]string), | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (c *mapString) Set(key, val string) { | ||||
| 	c.Lock() | ||||
| 	defer c.Unlock() | ||||
| 	c.Data[key] = val | ||||
| } | ||||
| func (c *mapString) Get(key string) string { | ||||
| 	c.RLock() | ||||
| 	defer c.RUnlock() | ||||
| 
 | ||||
| 	vals, ok := c.Data[key] | ||||
| 	if !ok { | ||||
| 		return "" | ||||
| 	} | ||||
| 
 | ||||
| 	return vals | ||||
| } | ||||
| 
 | ||||
| func (c *mapString) Del(key string) { | ||||
| 	c.Lock() | ||||
| 	defer c.Unlock() | ||||
| 
 | ||||
| 	delete(c.Data, key) | ||||
| } | ||||
| 
 | ||||
| func (c *mapString) Keys() (keys []string) { | ||||
| 	c.RLock() | ||||
| 	defer c.RUnlock() | ||||
| 
 | ||||
| 	for k, _ := range c.Data { | ||||
| 		keys = append(keys, k) | ||||
| 	} | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func (c *mapString) All() map[string]string { | ||||
| 	c.RLock() | ||||
| 	defer c.RUnlock() | ||||
| 
 | ||||
| 	return c.Data | ||||
| } | ||||
|  | @ -0,0 +1,14 @@ | |||
| package mem | ||||
| 
 | ||||
| import ( | ||||
| 	"git.apinb.com/bsm-sdk/core/vars" | ||||
| 	"github.com/FishGoddess/cachego" | ||||
| ) | ||||
| 
 | ||||
| func New() cachego.Cache { | ||||
| 	return cachego.NewCache( | ||||
| 		cachego.WithGC(vars.MemGcDuration), | ||||
| 		cachego.WithShardings(vars.MemShardings), | ||||
| 		cachego.WithLFU(vars.MemLRUMaxNumber), | ||||
| 	) | ||||
| } | ||||
|  | @ -1,90 +0,0 @@ | |||
| // Package redis 提供Redis缓存操作功能
 | ||||
| // 包括缓存设置、获取、删除等基本操作
 | ||||
| package redis | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"git.apinb.com/bsm-sdk/core/errcode" | ||||
| 	"git.apinb.com/bsm-sdk/core/vars" | ||||
| ) | ||||
| 
 | ||||
| // BuildKey 构建缓存键
 | ||||
| // prefix: 键前缀
 | ||||
| // params: 键参数
 | ||||
| // 返回: 完整的缓存键
 | ||||
| func (c *RedisClient) BuildKey(prefix string, params ...interface{}) string { | ||||
| 	key := vars.CacheKeyPrefix + prefix | ||||
| 	for _, param := range params { | ||||
| 		key += fmt.Sprintf(":%v", param) | ||||
| 	} | ||||
| 	return key | ||||
| } | ||||
| 
 | ||||
| // Get 获取缓存
 | ||||
| // key: 缓存键
 | ||||
| // result: 存储结果的指针
 | ||||
| // 返回: 错误信息
 | ||||
| func (c *RedisClient) Get(key string, result interface{}) error { | ||||
| 	if c.Client == nil { | ||||
| 		return errcode.ErrRedis | ||||
| 	} | ||||
| 
 | ||||
| 	data, err := c.Client.Get(c.Ctx, key).Result() | ||||
| 	if err != nil { | ||||
| 		return errcode.NewError(500, err.Error()) | ||||
| 	} | ||||
| 
 | ||||
| 	return json.Unmarshal([]byte(data), result) | ||||
| } | ||||
| 
 | ||||
| // Set 设置缓存
 | ||||
| // key: 缓存键
 | ||||
| // value: 缓存值
 | ||||
| // ttl: 过期时间
 | ||||
| // 返回: 错误信息
 | ||||
| func (c *RedisClient) Set(key string, value interface{}, ttl time.Duration) error { | ||||
| 	if c.Client == nil { | ||||
| 		return errcode.ErrRedis | ||||
| 	} | ||||
| 
 | ||||
| 	data, err := json.Marshal(value) | ||||
| 	if err != nil { | ||||
| 		return errcode.NewError(500, err.Error()) | ||||
| 	} | ||||
| 
 | ||||
| 	return c.Client.Set(c.Ctx, key, data, ttl).Err() | ||||
| } | ||||
| 
 | ||||
| // Delete 删除缓存
 | ||||
| // key: 缓存键
 | ||||
| // 返回: 错误信息
 | ||||
| func (c *RedisClient) Delete(key string) error { | ||||
| 	if c.Client == nil { | ||||
| 		return errcode.ErrRedis | ||||
| 	} | ||||
| 
 | ||||
| 	return c.Client.Del(c.Ctx, key).Err() | ||||
| } | ||||
| 
 | ||||
| // ClearAllCache 清除所有缓存
 | ||||
| // 返回: 错误信息
 | ||||
| func (c *RedisClient) ClearAllCache() error { | ||||
| 	if c.Client == nil { | ||||
| 		return errcode.ErrRedis | ||||
| 	} | ||||
| 
 | ||||
| 	pattern := vars.CacheKeyPrefix + "*" | ||||
| 	keys, err := c.Client.Keys(c.Ctx, pattern).Result() | ||||
| 	if err != nil { | ||||
| 		return errcode.NewError(500, err.Error()) | ||||
| 	} | ||||
| 
 | ||||
| 	if len(keys) > 0 { | ||||
| 		return c.Client.Del(c.Ctx, keys...).Err() | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
|  | @ -20,7 +20,6 @@ type RedisClient struct { | |||
| 	DB     int | ||||
| 	Client *cacheRedis.Client | ||||
| 	Ctx    context.Context | ||||
| 	memory map[string]any | ||||
| } | ||||
| 
 | ||||
| func New(dsn string, hashRadix string) *RedisClient { | ||||
|  | @ -55,7 +54,6 @@ func New(dsn string, hashRadix string) *RedisClient { | |||
| 		DB:     db, | ||||
| 		Client: client, | ||||
| 		Ctx:    context.Background(), | ||||
| 		memory: make(map[string]any), | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| package service | ||||
| package cmd | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
							
								
								
									
										62
									
								
								conf/new.go
								
								
								
								
							
							
						
						
									
										62
									
								
								conf/new.go
								
								
								
								
							|  | @ -1,5 +1,3 @@ | |||
| // Package conf 提供配置管理功能
 | ||||
| // 支持YAML配置文件加载、环境变量替换、配置验证等
 | ||||
| package conf | ||||
| 
 | ||||
| import ( | ||||
|  | @ -13,15 +11,12 @@ import ( | |||
| 	"math/rand/v2" | ||||
| 
 | ||||
| 	"git.apinb.com/bsm-sdk/core/env" | ||||
| 	"git.apinb.com/bsm-sdk/core/printer" | ||||
| 	"git.apinb.com/bsm-sdk/core/print" | ||||
| 	"git.apinb.com/bsm-sdk/core/utils" | ||||
| 	"git.apinb.com/bsm-sdk/core/vars" | ||||
| 	yaml "gopkg.in/yaml.v3" | ||||
| ) | ||||
| 
 | ||||
| // New 加载配置文件
 | ||||
| // srvKey: 服务键名
 | ||||
| // cfg: 配置结构体指针
 | ||||
| func New(srvKey string, cfg any) { | ||||
| 	env.NewEnv() | ||||
| 
 | ||||
|  | @ -31,18 +26,12 @@ func New(srvKey string, cfg any) { | |||
| 	// 获取主机名
 | ||||
| 	vars.HostName, _ = os.Hostname() | ||||
| 
 | ||||
| 	// 构造配置文件路径,输出配置文件信息
 | ||||
| 	// 构造配置文件路径,输出配置文件信息
 | ||||
| 	cfp := fmt.Sprintf("%s_%s.yaml", strings.ToLower(srvKey), env.Runtime.Mode) | ||||
| 	cfp = filepath.Join(env.Runtime.Prefix, "etc", cfp) | ||||
| 
 | ||||
| 	// 配置文件不存在则读取Workspace配置文件
 | ||||
| 	if !utils.PathExists(cfp) { | ||||
| 		cfp = fmt.Sprintf("workspace_%s_%s.yaml", strings.ToLower(env.Runtime.Workspace), env.Runtime.Mode) | ||||
| 		cfp = filepath.Join(env.Runtime.Prefix, "etc", cfp) | ||||
| 	} | ||||
| 
 | ||||
| 	printer.Info("[BSM - %s] Config File: %s", srvKey, cfp) | ||||
| 	printer.Info("[BSM - %s] Check Configure ...", vars.ServiceKey) | ||||
| 	print.Info("[BSM - %s] Config File: %s", srvKey, cfp) | ||||
| 	print.Info("[BSM - %s] Check Configure ...", vars.ServiceKey) | ||||
| 
 | ||||
| 	// 读取配置文件内容
 | ||||
| 	yamlFile, err := os.ReadFile(cfp) | ||||
|  | @ -50,23 +39,18 @@ func New(srvKey string, cfg any) { | |||
| 		log.Fatalf("ERROR: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	// 替换环境变量
 | ||||
| 	yamlString := os.ExpandEnv(string(yamlFile)) | ||||
| 
 | ||||
| 	// 检查配置文件中是否存在Service字段
 | ||||
| 	if !strings.Contains(yamlString, "Service:") { | ||||
| 	// 检查配置文件中是否存在Service和Port字段
 | ||||
| 	if !strings.Contains(string(yamlFile), "Service:") { | ||||
| 		log.Fatalln("ERROR: Service Not Nil", cfp) | ||||
| 	} | ||||
| 
 | ||||
| 	// 解析YAML到配置结构体
 | ||||
| 	err = yaml.Unmarshal([]byte(yamlString), cfg) | ||||
| 	// 解析YAML
 | ||||
| 	err = yaml.Unmarshal(yamlFile, cfg) | ||||
| 	if err != nil { | ||||
| 		log.Fatalf("ERROR: %v", err) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // NotNil 验证必需配置项不为空
 | ||||
| // values: 需要验证的配置值列表
 | ||||
| func NotNil(values ...string) { | ||||
| 	for _, value := range values { | ||||
| 		if strings.TrimSpace(value) == "" { | ||||
|  | @ -75,17 +59,12 @@ func NotNil(values ...string) { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| // PrintInfo 打印配置信息
 | ||||
| // addr: 服务地址
 | ||||
| func PrintInfo(addr string) { | ||||
| 	printer.Success("[BSM - %s] Config Check Success.", vars.ServiceKey) | ||||
| 	printer.Info("[BSM - %s] Service Name: %s", vars.ServiceKey, vars.ServiceKey) | ||||
| 	printer.Info("[BSM - %s] Runtime Mode: %s", vars.ServiceKey, env.Runtime.Mode) | ||||
| 	print.Success("[BSM - %s] Config Check Success.", vars.ServiceKey) | ||||
| 	print.Info("[BSM - %s] Service Name: %s", vars.ServiceKey, vars.ServiceKey) | ||||
| 	print.Info("[BSM - %s] Runtime Mode: %s", vars.ServiceKey, env.Runtime.Mode) | ||||
| } | ||||
| 
 | ||||
| // CheckPort 检查端口配置,如果为空则生成随机端口
 | ||||
| // port: 端口字符串
 | ||||
| // 返回: 有效的端口字符串
 | ||||
| func CheckPort(port string) string { | ||||
| 	if port == "" { | ||||
| 		r := rand.New(rand.NewPCG(1000, uint64(time.Now().UnixNano()))) | ||||
|  | @ -95,28 +74,9 @@ func CheckPort(port string) string { | |||
| 	return port | ||||
| } | ||||
| 
 | ||||
| // CheckIP 检查IP配置,如果为空则获取本机IP
 | ||||
| // ip: IP地址字符串
 | ||||
| // 返回: 有效的IP地址字符串
 | ||||
| func CheckIP(ip string) string { | ||||
| 	if ip == "" { | ||||
| 		return utils.GetLocationIP() | ||||
| 	} | ||||
| 	return ip | ||||
| } | ||||
| 
 | ||||
| // 初始化Logger配置
 | ||||
| func InitLoggerConf(cfg *LogConf) *LogConf { | ||||
| 	if cfg == nil { | ||||
| 		return &LogConf{ | ||||
| 			Name:     strings.ToLower(vars.ServiceKey), | ||||
| 			Level:    vars.LogLevel(vars.DEBUG), | ||||
| 			Dir:      "./logs/", | ||||
| 			Endpoint: "", | ||||
| 			Console:  true, | ||||
| 			File:     true, | ||||
| 			Remote:   false, | ||||
| 		} | ||||
| 	} | ||||
| 	return cfg | ||||
| } | ||||
|  |  | |||
|  | @ -1,16 +1,13 @@ | |||
| package conf | ||||
| 
 | ||||
| import "git.apinb.com/bsm-sdk/core/vars" | ||||
| 
 | ||||
| type Base struct { | ||||
| 	Service        string   `yaml:"Service"`   // 服务名称
 | ||||
| 	Port           string   `yaml:"Port"`      // 服务监听端口,0为自动随机端口
 | ||||
| 	Cache          string   `yaml:"Cache"`     // REDIS缓存
 | ||||
| 	SecretKey      string   `yaml:"SecretKey"` // 服务秘钥
 | ||||
| 	BindIP         string   `yaml:"BindIP"`    // 绑定IP
 | ||||
| 	Addr           string   `yaml:"Addr"` | ||||
| 	Log            *LogConf `yaml:"Log"` // 日志配置
 | ||||
| 	OnMicroService bool     `yaml:"OnMicroService"` | ||||
| 	Service        string `yaml:"Service"`   // 服务名称
 | ||||
| 	Port           string `yaml:"Port"`      // 服务监听端口,0为自动随机端口
 | ||||
| 	Cache          string `yaml:"Cache"`     // REDIS缓存
 | ||||
| 	SecretKey      string `yaml:"SecretKey"` // 服务秘钥
 | ||||
| 	BindIP         string `yaml:"BindIP"`    // 绑定IP
 | ||||
| 	Addr           string `yaml:"Addr"` | ||||
| 	OnMicroService bool   `yaml:"OnMicroService"` | ||||
| } | ||||
| 
 | ||||
| type DBConf struct { | ||||
|  | @ -72,24 +69,3 @@ type TlsConf struct { | |||
| 	CertFile string // 证书文件路径
 | ||||
| 	KeyFile  string // 密钥文件路径
 | ||||
| } | ||||
| 
 | ||||
| type WebSocketConf struct { | ||||
| 	ReadBufferSize   int    `yaml:"ReadBufferSize"` | ||||
| 	WriteBufferSize  int    `yaml:"WriteBufferSize"` | ||||
| 	CheckOrigin      bool   `yaml:"CheckOrigin"` | ||||
| 	PingPeriod       string `yaml:"PingPeriod"` | ||||
| 	PongWait         string `yaml:"PongWait"` | ||||
| 	WriteWait        string `yaml:"WriteWait"` | ||||
| 	MaxMessageSize   int64  `yaml:"MaxMessageSize"` | ||||
| 	HandshakeTimeout string `yaml:"HandshakeTimeout"` | ||||
| } | ||||
| 
 | ||||
| type LogConf struct { | ||||
| 	Name     string        `yaml:"Name"` | ||||
| 	Level    vars.LogLevel `yaml:"Level"` | ||||
| 	Dir      string        `yaml:"Dir"` | ||||
| 	Endpoint string        `yaml:"Endpoint"` | ||||
| 	Console  bool          `yaml:"Console"` | ||||
| 	File     bool          `yaml:"File"` | ||||
| 	Remote   bool          `yaml:"Remote"` | ||||
| } | ||||
|  |  | |||
|  | @ -1,5 +1,3 @@ | |||
| // Package aes 提供AES加密解密功能
 | ||||
| // 支持GCM、CBC、ECB等多种加密模式
 | ||||
| package aes | ||||
| 
 | ||||
| import ( | ||||
|  | @ -15,11 +13,8 @@ import ( | |||
| 	"os" | ||||
| ) | ||||
| 
 | ||||
| // =================== GCM模式 ======================
 | ||||
| // AESGCMEncrypt AES GCM模式加密
 | ||||
| // plaintext: 明文数据
 | ||||
| // key: 加密密钥
 | ||||
| // 返回: 十六进制编码的密文字符串
 | ||||
| // =================== GCM ======================
 | ||||
| // AEC GCM 加密
 | ||||
| func AESGCMEncrypt(plaintext, key []byte) (string, error) { | ||||
| 	block, err := aes.NewCipher(key) | ||||
| 	if err != nil { | ||||
|  | @ -37,10 +32,7 @@ func AESGCMEncrypt(plaintext, key []byte) (string, error) { | |||
| 	return hex.EncodeToString(ciphertext), nil | ||||
| } | ||||
| 
 | ||||
| // AESGCMDecrypt AES GCM模式解密
 | ||||
| // ciphertext: 十六进制编码的密文字符串
 | ||||
| // key: 解密密钥
 | ||||
| // 返回: 解密后的明文数据
 | ||||
| // AEC GCM 解密
 | ||||
| func AESGCMDecrypt(ciphertext string, key []byte) ([]byte, error) { | ||||
| 	data, err := hex.DecodeString(ciphertext) | ||||
| 	if err != nil { | ||||
|  | @ -62,12 +54,8 @@ func AESGCMDecrypt(ciphertext string, key []byte) ([]byte, error) { | |||
| 	return gcm.Open(nil, nonce, cipherbyte, nil) | ||||
| } | ||||
| 
 | ||||
| // =================== CBC模式 ======================
 | ||||
| // Encrypt AES CBC模式加密
 | ||||
| // key: Base64编码的密钥
 | ||||
| // iv: Base64编码的初始化向量
 | ||||
| // data: 要加密的数据
 | ||||
| // 返回: Base64编码的密文
 | ||||
| // =================== CBC ======================
 | ||||
| // AES CBC加密
 | ||||
| func Encrypt(key string, iv string, data string) string { | ||||
| 	if len(data) == 0 { | ||||
| 		return "" | ||||
|  | @ -84,11 +72,7 @@ func Encrypt(key string, iv string, data string) string { | |||
| 	return data | ||||
| } | ||||
| 
 | ||||
| // Decrypt AES CBC模式解密
 | ||||
| // key: Base64编码的密钥
 | ||||
| // iv: Base64编码的初始化向量
 | ||||
| // data: Base64编码的密文
 | ||||
| // 返回: 解密后的明文
 | ||||
| // AES CBC解密
 | ||||
| func Decrypt(key string, iv string, data string) string { | ||||
| 	if len(data) == 0 { | ||||
| 		return "" | ||||
|  | @ -107,19 +91,11 @@ func Decrypt(key string, iv string, data string) string { | |||
| 	return data | ||||
| } | ||||
| 
 | ||||
| // _PKCS5Padding PKCS5填充
 | ||||
| // cipherText: 需要填充的数据
 | ||||
| // blockSize: 块大小
 | ||||
| // 返回: 填充后的数据
 | ||||
| func _PKCS5Padding(cipherText []byte, blockSize int) []byte { | ||||
| 	padding := blockSize - len(cipherText)%blockSize | ||||
| 	padText := bytes.Repeat([]byte{byte(padding)}, padding) | ||||
| 	return append(cipherText, padText...) | ||||
| } | ||||
| 
 | ||||
| // _PKCS5UnPadding PKCS5去填充
 | ||||
| // origData: 需要去填充的数据
 | ||||
| // 返回: 去填充后的数据
 | ||||
| func _PKCS5UnPadding(origData []byte) []byte { | ||||
| 	length := len(origData) | ||||
| 	unpadding := int(origData[length-1]) | ||||
|  | @ -129,11 +105,7 @@ func _PKCS5UnPadding(origData []byte) []byte { | |||
| 	return origData[:(length - unpadding)] | ||||
| } | ||||
| 
 | ||||
| // =================== ECB模式 ======================
 | ||||
| // AesEncryptECB AES ECB模式加密
 | ||||
| // origData: 原始数据
 | ||||
| // key: 加密密钥
 | ||||
| // 返回: Base64编码的密文
 | ||||
| // =================== ECB ======================
 | ||||
| func AesEncryptECB(origData []byte, key []byte) (data string) { | ||||
| 	cipher, _ := aes.NewCipher(generateKey(key)) | ||||
| 	length := (len(origData) + aes.BlockSize) / aes.BlockSize | ||||
|  | @ -152,16 +124,11 @@ func AesEncryptECB(origData []byte, key []byte) (data string) { | |||
| 	data = base64.StdEncoding.EncodeToString(encrypted) | ||||
| 	return data | ||||
| } | ||||
| 
 | ||||
| // AesDecryptECB AES ECB模式解密
 | ||||
| // encrypted: Base64编码的密文
 | ||||
| // key: 解密密钥
 | ||||
| // 返回: 解密后的明文数据
 | ||||
| func AesDecryptECB(encrypted string, key []byte) (decrypted []byte) { | ||||
| 	decodedCiphertext, _ := base64.StdEncoding.DecodeString(encrypted) | ||||
| 	cipher, _ := aes.NewCipher(generateKey(key)) | ||||
| 	decrypted = make([]byte, len(decodedCiphertext)) | ||||
| 	// 分组分块解密
 | ||||
| 	//
 | ||||
| 	for bs, be := 0, cipher.BlockSize(); bs < len(decodedCiphertext); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() { | ||||
| 		cipher.Decrypt(decrypted[bs:be], decodedCiphertext[bs:be]) | ||||
| 	} | ||||
|  | @ -173,10 +140,6 @@ func AesDecryptECB(encrypted string, key []byte) (decrypted []byte) { | |||
| 
 | ||||
| 	return decrypted[:trim] | ||||
| } | ||||
| 
 | ||||
| // generateKey 生成标准长度的密钥
 | ||||
| // key: 原始密钥
 | ||||
| // 返回: 16字节的标准密钥
 | ||||
| func generateKey(key []byte) (genKey []byte) { | ||||
| 	genKey = make([]byte, 16) | ||||
| 	copy(genKey, key) | ||||
|  | @ -188,16 +151,12 @@ func generateKey(key []byte) (genKey []byte) { | |||
| 	return genKey | ||||
| } | ||||
| 
 | ||||
| // AesKeyCheck 检查AES密钥环境变量
 | ||||
| // key: 环境变量名
 | ||||
| // 返回: 十六进制编码的密钥字符串
 | ||||
| func AesKeyCheck(key string) (string, error) { | ||||
| 	// 从环境变量获取密钥
 | ||||
| 	keyHex := os.Getenv(key) | ||||
| 	if keyHex == "" { | ||||
| 		// 使用入参作为变量名,避免硬编码误导
 | ||||
| 		fmt.Printf("环境变量 %s 未设置\n", key) | ||||
| 		return "", errors.New("密钥环境变量未设置") | ||||
| 		fmt.Println("环境变量 RST_KEY 未设置") | ||||
| 		return "", errors.New("环境变量 RST_KEY 未设置") | ||||
| 	} | ||||
| 	// 解码十六进制字符串的密钥
 | ||||
| 	byteKey, err := hex.DecodeString(keyHex) | ||||
|  | @ -206,8 +165,8 @@ func AesKeyCheck(key string) (string, error) { | |||
| 		return "", errors.New("密钥解码失败") | ||||
| 	} | ||||
| 	// 检查密钥长度
 | ||||
| 	if len(byteKey) != 16 && len(byteKey) != 24 && len(byteKey) != 32 { | ||||
| 		fmt.Printf("无效的密钥长度: %d 字节 (需要16,24或32字节)\n", len(byteKey)) | ||||
| 	if len(byteKey) != 16 && len(key) != 24 && len(key) != 32 { | ||||
| 		fmt.Printf("无效的密钥长度: %d 字节 (需要16,24或32字节)\n", len(key)) | ||||
| 		return "", errors.New("无效的密钥长度,需要16,24或32字节") | ||||
| 	} | ||||
| 	return keyHex, nil | ||||
|  |  | |||
|  | @ -28,7 +28,7 @@ func New(secret string) { | |||
| 
 | ||||
| func GenerateTokenAes(id uint, identity, client, role string, owner any, extend map[string]string) (string, error) { | ||||
| 	if !(JwtSecretLen == 16 || JwtSecretLen == 24 || JwtSecretLen == 32) { | ||||
| 		return "", errcode.ErrTokenSecretKey | ||||
| 		return "", errcode.ErrJWTSecretKey | ||||
| 	} | ||||
| 	expireTime := time.Now().Add(vars.JwtExpire) | ||||
| 	claims := types.JwtClaims{ | ||||
|  | @ -43,7 +43,7 @@ func GenerateTokenAes(id uint, identity, client, role string, owner any, extend | |||
| 
 | ||||
| 	byte, err := json.Marshal(claims) | ||||
| 	if err != nil { | ||||
| 		return "", errcode.ErrTokenJsonEncode | ||||
| 		return "", errcode.ErrJWTJsonEncode | ||||
| 	} | ||||
| 
 | ||||
| 	token, err := AesEncryptCBC(byte) | ||||
|  | @ -59,7 +59,7 @@ func AesEncryptCBC(plan []byte) (string, error) { | |||
| 	// NewCipher该函数限制了输入k的长度必须为16, 24或者32
 | ||||
| 	block, err := aes.NewCipher(JwtSecret) | ||||
| 	if err != nil { | ||||
| 		return "", errcode.ErrTokenSecretKey | ||||
| 		return "", errcode.ErrJWTSecretKey | ||||
| 	} | ||||
| 	// 获取秘钥块的长度
 | ||||
| 	blockSize := block.BlockSize() | ||||
|  | @ -76,17 +76,17 @@ func AesEncryptCBC(plan []byte) (string, error) { | |||
| 
 | ||||
| func AesDecryptCBC(cryted string) (b []byte, err error) { | ||||
| 	if (JwtSecretLen == 16 || JwtSecretLen == 24 || JwtSecretLen == 32) == false { | ||||
| 		return nil, errcode.ErrTokenSecretKey | ||||
| 		return nil, errcode.ErrJWTSecretKey | ||||
| 	} | ||||
| 	// 转成字节数组
 | ||||
| 	crytedByte, err := base64.StdEncoding.DecodeString(cryted) | ||||
| 	if err != nil { | ||||
| 		return nil, errcode.ErrTokenBase64Decode | ||||
| 		return nil, errcode.ErrJWTBase64Decode | ||||
| 	} | ||||
| 	// 分组秘钥
 | ||||
| 	block, err := aes.NewCipher(JwtSecret) | ||||
| 	if err != nil { | ||||
| 		return nil, errcode.ErrTokenSecretKey | ||||
| 		return nil, errcode.ErrJWTSecretKey | ||||
| 	} | ||||
| 	// 获取秘钥块的长度
 | ||||
| 	blockSize := block.BlockSize() | ||||
|  | @ -99,46 +99,36 @@ func AesDecryptCBC(cryted string) (b []byte, err error) { | |||
| 	// 去补全码
 | ||||
| 	orig = PKCS7UnPadding(orig, blockSize) | ||||
| 	if orig == nil { | ||||
| 		return nil, errcode.ErrTokenAuthParseFail | ||||
| 		return nil, errcode.ErrJWTAuthParseFail | ||||
| 	} | ||||
| 	return orig, nil | ||||
| } | ||||
| 
 | ||||
| // PKCS7Padding 使用PKCS7填充法对明文进行填充
 | ||||
| func PKCS7Padding(ciphertext []byte, blocksize int) []byte { | ||||
| 	padding := blocksize - len(ciphertext)%blocksize | ||||
| 	padtext := bytes.Repeat([]byte{byte(padding)}, padding) | ||||
| 	return append(ciphertext, padtext...) | ||||
| } | ||||
| 
 | ||||
| // PKCS7UnPadding 去除PKCS7填充
 | ||||
| // 去码
 | ||||
| // bug:runtime error: slice bounds out of range [:-22]
 | ||||
| func PKCS7UnPadding(origData []byte, blocksize int) []byte { | ||||
| 	// 检查块大小是否有效
 | ||||
| 	if blocksize <= 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	// 检查原始数据是否为空
 | ||||
| 	if origData == nil || len(origData) == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	// 检查数据长度是否为块大小的整数倍
 | ||||
| 	if len(origData)%blocksize != 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	length := len(origData) | ||||
| 	// 获取填充字节数
 | ||||
| 	unpadding := int(origData[length-1]) | ||||
| 
 | ||||
| 	// 检查去填充后数据长度是否有效
 | ||||
| 	if length-unpadding <= 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	// 返回去除填充后的数据
 | ||||
| 	return origData[:(length - unpadding)] | ||||
| } | ||||
| 
 | ||||
|  | @ -152,12 +142,12 @@ func ParseTokenAes(token string) (*types.JwtClaims, error) { | |||
| 	var ac *types.JwtClaims | ||||
| 	err = json.Unmarshal(data, &ac) | ||||
| 	if err != nil { | ||||
| 		return nil, errcode.ErrTokenAuthParseFail | ||||
| 		return nil, errcode.ErrJWTAuthParseFail | ||||
| 	} | ||||
| 
 | ||||
| 	expireTime := time.Now().Unix() | ||||
| 	if expireTime > ac.ExpiresAt { | ||||
| 		return nil, errcode.ErrTokenAuthExpire | ||||
| 		return nil, errcode.ErrJWTAuthExpire | ||||
| 	} | ||||
| 
 | ||||
| 	return ac, nil | ||||
|  |  | |||
|  | @ -1,98 +0,0 @@ | |||
| package token | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/base64" | ||||
| 	"encoding/json" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"git.apinb.com/bsm-sdk/core/errcode" | ||||
| 	"git.apinb.com/bsm-sdk/core/vars" | ||||
| 	"github.com/golang-jwt/jwt/v5" | ||||
| ) | ||||
| 
 | ||||
| type Claims struct { | ||||
| 	ID                   uint              `json:"id"` | ||||
| 	Identity             string            `json:"identity"` | ||||
| 	Extend               map[string]string `json:"extend"` | ||||
| 	Client               string            `json:"client"` | ||||
| 	Owner                any               `json:"owner"` | ||||
| 	Role                 string            `json:"role"` | ||||
| 	jwt.RegisteredClaims                   // v5版本新加的方法
 | ||||
| } | ||||
| 
 | ||||
| type tokenJwt struct { | ||||
| 	SecretKey string | ||||
| } | ||||
| 
 | ||||
| func New(secretKey string) *tokenJwt { | ||||
| 	return &tokenJwt{SecretKey: secretKey} | ||||
| } | ||||
| 
 | ||||
| // 生成JWT
 | ||||
| func (t *tokenJwt) GenerateJwt(id uint, identity, client, role string, owner any, extend map[string]string) (string, error) { | ||||
| 	keyLen := len(t.SecretKey) | ||||
| 	if !(keyLen == 16 || keyLen == 24 || keyLen == 32) { | ||||
| 		return "", errcode.ErrTokenSecretKey | ||||
| 	} | ||||
| 
 | ||||
| 	now := time.Now() | ||||
| 
 | ||||
| 	claims := Claims{ | ||||
| 		ID:       id, | ||||
| 		Identity: identity, | ||||
| 		Client:   client, | ||||
| 		Extend:   extend, | ||||
| 		Owner:    owner, | ||||
| 		Role:     role, | ||||
| 		RegisteredClaims: jwt.RegisteredClaims{ | ||||
| 			ExpiresAt: jwt.NewNumericDate(now.Add(vars.JwtExpire)), // 过期时间24小时
 | ||||
| 			IssuedAt:  jwt.NewNumericDate(now),                     // 签发时间
 | ||||
| 			NotBefore: jwt.NewNumericDate(now),                     // 生效时间
 | ||||
| 		}, | ||||
| 	} | ||||
| 	// 使用HS256签名算法
 | ||||
| 	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) | ||||
| 	s, err := token.SignedString([]byte(t.SecretKey)) | ||||
| 	if err != nil { | ||||
| 		return "", errcode.String(errcode.ErrTokenGenerate, err.Error()) | ||||
| 	} | ||||
| 	return s, nil | ||||
| } | ||||
| 
 | ||||
| // 解析JWT
 | ||||
| func (t *tokenJwt) ParseJwt(tokenstring string) (*Claims, error) { | ||||
| 	token, err := jwt.ParseWithClaims(tokenstring, &Claims{}, func(token *jwt.Token) (interface{}, error) { | ||||
| 		return []byte(t.SecretKey), nil | ||||
| 	}) | ||||
| 	if claims, ok := token.Claims.(*Claims); ok && token.Valid { | ||||
| 		return claims, nil | ||||
| 	} else { | ||||
| 		return nil, errcode.String(errcode.ErrTokenParse, err.Error()) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // 验证JWT是否过期
 | ||||
| func (t *tokenJwt) IsExpired(tokenstring string) (bool, error) { | ||||
| 	// 分割JWT的三个部分
 | ||||
| 	parts := strings.Split(tokenstring, ".") | ||||
| 	if len(parts) != 3 { | ||||
| 		return true, errcode.ErrTokenDataInvalid | ||||
| 	} | ||||
| 
 | ||||
| 	// 解码Payload部分
 | ||||
| 	payload, err := base64.RawURLEncoding.DecodeString(parts[1]) | ||||
| 	if err != nil { | ||||
| 		return true, errcode.String(errcode.ErrTokenBase64Decode, err.Error()) | ||||
| 	} | ||||
| 
 | ||||
| 	// 解析JSON
 | ||||
| 	var claims jwt.RegisteredClaims | ||||
| 	if err := json.Unmarshal(payload, &claims); err != nil { | ||||
| 		return true, errcode.String(errcode.ErrTokenJsonDecode, err.Error()) | ||||
| 	} | ||||
| 
 | ||||
| 	// 检查过期时间
 | ||||
| 	currentTime := time.Now().Unix() | ||||
| 	return claims.ExpiresAt.Unix() < currentTime, nil | ||||
| } | ||||
|  | @ -1,32 +1,34 @@ | |||
| package mapsync | ||||
| package data | ||||
| 
 | ||||
| import ( | ||||
| 	"sync" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	// sync map
 | ||||
| 	MapFloat *mapFloat | ||||
| 	// Cache
 | ||||
| 	CacheMapFloat *MapFloat | ||||
| ) | ||||
| 
 | ||||
| // lock
 | ||||
| type mapFloat struct { | ||||
| type MapFloat struct { | ||||
| 	sync.RWMutex | ||||
| 	Data map[string]float64 | ||||
| } | ||||
| 
 | ||||
| func NewMapFloat() *mapFloat { | ||||
| 	return &mapFloat{ | ||||
| func NewMapFloat() *MapFloat { | ||||
| 	return &MapFloat{ | ||||
| 		Data: make(map[string]float64), | ||||
| 	} | ||||
| } | ||||
| func (c *mapFloat) Set(key string, val float64) { | ||||
| 	c.Lock() | ||||
| 	defer c.Unlock() | ||||
| 	c.Data[key] = val | ||||
| 
 | ||||
| func (c *MapFloat) All() map[string]float64 { | ||||
| 	c.RLock() | ||||
| 	defer c.RUnlock() | ||||
| 
 | ||||
| 	return c.Data | ||||
| } | ||||
| 
 | ||||
| func (c *mapFloat) Get(key string) float64 { | ||||
| func (c *MapFloat) Get(key string) float64 { | ||||
| 	c.RLock() | ||||
| 	defer c.RUnlock() | ||||
| 
 | ||||
|  | @ -38,14 +40,13 @@ func (c *mapFloat) Get(key string) float64 { | |||
| 	return vals | ||||
| } | ||||
| 
 | ||||
| func (c *mapFloat) Del(key string) { | ||||
| func (c *MapFloat) Set(key string, val float64) { | ||||
| 	c.Lock() | ||||
| 	defer c.Unlock() | ||||
| 
 | ||||
| 	delete(c.Data, key) | ||||
| 	c.Data[key] = val | ||||
| } | ||||
| 
 | ||||
| func (c *mapFloat) Keys() (keys []string) { | ||||
| func (c *MapFloat) Keys() (keys []string) { | ||||
| 	c.RLock() | ||||
| 	defer c.RUnlock() | ||||
| 
 | ||||
|  | @ -55,10 +56,3 @@ func (c *mapFloat) Keys() (keys []string) { | |||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func (c *mapFloat) All() map[string]float64 { | ||||
| 	c.RLock() | ||||
| 	defer c.RUnlock() | ||||
| 
 | ||||
| 	return c.Data | ||||
| } | ||||
|  | @ -0,0 +1,51 @@ | |||
| package data | ||||
| 
 | ||||
| import ( | ||||
| 	"sync" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	// Cache
 | ||||
| 	CacheMapString *MapString | ||||
| ) | ||||
| 
 | ||||
| // lock
 | ||||
| type MapString struct { | ||||
| 	sync.RWMutex | ||||
| 	Data map[string]string | ||||
| } | ||||
| 
 | ||||
| func NewMapString() *MapString { | ||||
| 	return &MapString{ | ||||
| 		Data: make(map[string]string), | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (c *MapString) Get(key string) string { | ||||
| 	c.RLock() | ||||
| 	defer c.RUnlock() | ||||
| 
 | ||||
| 	vals, ok := c.Data[key] | ||||
| 	if !ok { | ||||
| 		return "" | ||||
| 	} | ||||
| 
 | ||||
| 	return vals | ||||
| } | ||||
| 
 | ||||
| func (c *MapString) Set(key, val string) { | ||||
| 	c.Lock() | ||||
| 	defer c.Unlock() | ||||
| 	c.Data[key] = val | ||||
| } | ||||
| 
 | ||||
| func (c *MapString) Keys() (keys []string) { | ||||
| 	c.RLock() | ||||
| 	defer c.RUnlock() | ||||
| 
 | ||||
| 	for k, _ := range c.Data { | ||||
| 		keys = append(keys, k) | ||||
| 	} | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
|  | @ -9,9 +9,9 @@ import ( | |||
| 	"time" | ||||
| 
 | ||||
| 	"git.apinb.com/bsm-sdk/core/vars" | ||||
| 	"github.com/elastic/go-elasticsearch/v9" | ||||
| 	"github.com/elastic/go-elasticsearch/v9/esapi" | ||||
| 	"github.com/elastic/go-elasticsearch/v9/esutil" | ||||
| 	"github.com/elastic/go-elasticsearch/v8" | ||||
| 	"github.com/elastic/go-elasticsearch/v8/esapi" | ||||
| 	"github.com/elastic/go-elasticsearch/v8/esutil" | ||||
| ) | ||||
| 
 | ||||
| type ES struct { | ||||
|  |  | |||
							
								
								
									
										145
									
								
								database/new.go
								
								
								
								
							
							
						
						
									
										145
									
								
								database/new.go
								
								
								
								
							|  | @ -1,145 +0,0 @@ | |||
| // Package database 提供数据库连接和管理功能
 | ||||
| // 支持MySQL和PostgreSQL数据库,包含连接池管理和自动迁移
 | ||||
| package database | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"git.apinb.com/bsm-sdk/core/database/sql" | ||||
| 	"git.apinb.com/bsm-sdk/core/types" | ||||
| 	"git.apinb.com/bsm-sdk/core/vars" | ||||
| 	"gorm.io/driver/mysql" | ||||
| 	"gorm.io/gorm" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	// MigrateTables 存储需要在数据库初始化时自动迁移的表
 | ||||
| 	MigrateTables []any | ||||
| ) | ||||
| 
 | ||||
| // NewDatabase 根据提供的驱动类型创建新的数据库连接
 | ||||
| // 支持MySQL和PostgreSQL数据库
 | ||||
| // driver: 数据库驱动类型 ("mysql" 或 "postgres")
 | ||||
| // dsn: 数据源名称数组
 | ||||
| // options: 数据库连接选项
 | ||||
| // 返回: GORM数据库实例
 | ||||
| func NewDatabase(driver string, dsn []string, options *types.SqlOptions) (db *gorm.DB, err error) { | ||||
| 	driver = strings.ToLower(driver) | ||||
| 	switch driver { | ||||
| 	case "mysql": | ||||
| 		db, err = NewMysql(dsn, options) | ||||
| 	case "postgres": | ||||
| 		db, err = NewPostgres(dsn, options) | ||||
| 	default: | ||||
| 		return nil, fmt.Errorf("unsupported database driver: %s", driver) | ||||
| 	} | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	// 自动迁移表结构
 | ||||
| 	if len(MigrateTables) > 0 { | ||||
| 		err = db.AutoMigrate(MigrateTables...) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return db, nil | ||||
| } | ||||
| 
 | ||||
| // NewMysql 创建MySQL数据库服务
 | ||||
| // dsn: 数据源名称数组
 | ||||
| // options: 数据库连接选项
 | ||||
| // 返回: GORM数据库实例
 | ||||
| func NewMysql(dsn []string, options *types.SqlOptions) (gormDb *gorm.DB, err error) { | ||||
| 	// 设置连接默认值
 | ||||
| 	if options == nil { | ||||
| 		options = &types.SqlOptions{ | ||||
| 			MaxIdleConns:    vars.SqlOptionMaxIdleConns, | ||||
| 			MaxOpenConns:    vars.SqlOptionMaxOpenConns, | ||||
| 			ConnMaxLifetime: vars.SqlOptionConnMaxLifetime, | ||||
| 			LogStdout:       false, | ||||
| 			Debug:           true, | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	gormDb, err = gorm.Open(mysql.Open(dsn[0]), &gorm.Config{ | ||||
| 		SkipDefaultTransaction: true, | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	if options.Debug { | ||||
| 		gormDb = gormDb.Debug() | ||||
| 	} | ||||
| 
 | ||||
| 	// 获取通用数据库对象 sql.DB,然后使用其提供的功能
 | ||||
| 	sqlDB, err := gormDb.DB() | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	// SetMaxIdleConns 用于设置连接池中空闲连接的最大数量
 | ||||
| 	sqlDB.SetMaxIdleConns(options.MaxIdleConns) | ||||
| 	// SetMaxOpenConns 设置打开数据库连接的最大数量
 | ||||
| 	sqlDB.SetMaxOpenConns(options.MaxOpenConns) | ||||
| 	// SetConnMaxLifetime 设置了连接可复用的最大时间
 | ||||
| 	sqlDB.SetConnMaxLifetime(options.ConnMaxLifetime) | ||||
| 
 | ||||
| 	return gormDb, nil | ||||
| } | ||||
| 
 | ||||
| // NewPostgres 创建PostgreSQL数据库服务
 | ||||
| // dsn: 数据源名称数组
 | ||||
| // options: 数据库连接选项
 | ||||
| // 返回: GORM数据库实例
 | ||||
| func NewPostgres(dsn []string, options *types.SqlOptions) (gormDb *gorm.DB, err error) { | ||||
| 	// 设置连接默认值
 | ||||
| 	if options == nil { | ||||
| 		options = &types.SqlOptions{ | ||||
| 			MaxIdleConns:    vars.SqlOptionMaxIdleConns, | ||||
| 			MaxOpenConns:    vars.SqlOptionMaxOpenConns, | ||||
| 			ConnMaxLifetime: vars.SqlOptionConnMaxLifetime, | ||||
| 			LogStdout:       false, | ||||
| 			Debug:           true, | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	gormDb, err = sql.NewPostgreSql(dsn[0], options) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	if options.Debug { | ||||
| 		gormDb = gormDb.Debug() | ||||
| 	} | ||||
| 
 | ||||
| 	// 获取通用数据库对象 sql.DB,然后使用其提供的功能
 | ||||
| 	sqlDB, err := gormDb.DB() | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	// SetMaxIdleConns 用于设置连接池中空闲连接的最大数量
 | ||||
| 	sqlDB.SetMaxIdleConns(options.MaxIdleConns) | ||||
| 	// SetMaxOpenConns 设置打开数据库连接的最大数量
 | ||||
| 	sqlDB.SetMaxOpenConns(options.MaxOpenConns) | ||||
| 	// SetConnMaxLifetime 设置了连接可复用的最大时间
 | ||||
| 	sqlDB.SetConnMaxLifetime(options.ConnMaxLifetime) | ||||
| 
 | ||||
| 	return gormDb, nil | ||||
| } | ||||
| 
 | ||||
| // AppendMigrate 调用此函数后,会在数据库初始化时自动迁移表结构
 | ||||
| //
 | ||||
| // - table: 需要自动迁移的表
 | ||||
| func AppendMigrate(table any) { | ||||
| 	if MigrateTables == nil { | ||||
| 		MigrateTables = make([]any, 0) | ||||
| 	} | ||||
| 	MigrateTables = append(MigrateTables, table) | ||||
| } | ||||
|  | @ -12,7 +12,7 @@ func SetOptions(options *types.SqlOptions) *types.SqlOptions { | |||
| 	if options == nil { | ||||
| 		options = &types.SqlOptions{ | ||||
| 			MaxIdleConns:    vars.SqlOptionMaxIdleConns, | ||||
| 			MaxOpenConns:    vars.SqlOptionMaxOpenConns, | ||||
| 			MaxOpenConns:    vars.SqlOptionMaxIdleConns, | ||||
| 			ConnMaxLifetime: vars.SqlOptionConnMaxLifetime, | ||||
| 			LogStdout:       false, | ||||
| 			Debug:           false, | ||||
|  | @ -30,7 +30,7 @@ func NewPostgreSql(dsn string, options *types.SqlOptions) (*gorm.DB, error) { | |||
| 	if options == nil { | ||||
| 		options = &types.SqlOptions{ | ||||
| 			MaxIdleConns:    vars.SqlOptionMaxIdleConns, | ||||
| 			MaxOpenConns:    vars.SqlOptionMaxOpenConns, | ||||
| 			MaxOpenConns:    vars.SqlOptionMaxIdleConns, | ||||
| 			ConnMaxLifetime: vars.SqlOptionConnMaxLifetime, | ||||
| 			LogStdout:       false, | ||||
| 			Debug:           true, | ||||
|  | @ -56,11 +56,7 @@ func NewPostgreSql(dsn string, options *types.SqlOptions) (*gorm.DB, error) { | |||
| 	} | ||||
| 
 | ||||
| 	// 获取通用数据库对象 sql.DB ,然后使用其提供的功能
 | ||||
| 	sqlDB, err := gormDb.DB() | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	 | ||||
| 	sqlDB, _ := gormDb.DB() | ||||
| 	// SetMaxIdleConns 用于设置连接池中空闲连接的最大数量。
 | ||||
| 	sqlDB.SetMaxIdleConns(options.MaxIdleConns) | ||||
| 	// SetMaxOpenConns 设置打开数据库连接的最大数量。
 | ||||
|  |  | |||
|  | @ -15,7 +15,7 @@ var Runtime *types.RuntimeEnv = nil | |||
| func NewEnv() *types.RuntimeEnv { | ||||
| 	if Runtime == nil { | ||||
| 		Runtime = &types.RuntimeEnv{ | ||||
| 			Workspace:    GetEnvDefault("BSM_Workspace", "default"), | ||||
| 			Workspace:    GetEnvDefault("BSM_Workspace", "def"), | ||||
| 			JwtSecretKey: GetEnvDefault("BSM_JwtSecretKey", "Cblocksmesh2022C"), | ||||
| 			Mode:         strings.ToLower(GetEnvDefault("BSM_RuntimeMode", "dev")), | ||||
| 			LicencePath:  strings.ToLower(GetEnvDefault("BSM_Licence", "")), | ||||
|  |  | |||
|  | @ -1,5 +1,3 @@ | |||
| // Package errcode 提供统一的错误码管理
 | ||||
| // 定义了系统中所有可能的错误类型和对应的错误码
 | ||||
| package errcode | ||||
| 
 | ||||
| import ( | ||||
|  | @ -7,108 +5,83 @@ import ( | |||
| 	"google.golang.org/grpc/status" | ||||
| ) | ||||
| 
 | ||||
| // HTTP请求头相关错误码,起始码:1000
 | ||||
| // header error code ,start:100
 | ||||
| var ( | ||||
| 	AllErrors              = make(map[int]string) | ||||
| 	ErrHeaderRequestId     = NewError(1001, "Header Request-Id Not Found")    // 请求ID头缺失
 | ||||
| 	ErrHeaderAuthorization = NewError(1002, "Header Authorization Not Found") // 授权头缺失
 | ||||
| 	ErrHeaderSecretKey     = NewError(1003, "Header Secret-Key Not Found")    // 密钥头缺失
 | ||||
| 	ErrHeaderMustParams    = NewError(1004, "Header Must Params")             // 必需参数头缺失
 | ||||
| 	ErrHeaderRequestId     = NewError(101, "Header Request-Id Not Found") | ||||
| 	ErrHeaderAuthorization = NewError(102, "Header Authorization Not Found") | ||||
| 	ErrHeaderSecretKey     = NewError(103, "Header Secret-Key Not Found") | ||||
| 	ErrHeaderMustParams    = NewError(104, "Header Must Params") | ||||
| ) | ||||
| 
 | ||||
| // 标准业务错误码,起始码:1100
 | ||||
| // standard error code ,start:110
 | ||||
| var ( | ||||
| 	ErrEmpty           = NewError(1101, "Data Is Empty")           // 数据为空
 | ||||
| 	ErrRequestParse    = NewError(1102, "Request Parse Fail")      // 请求解析失败
 | ||||
| 	ErrRequestMust     = NewError(1103, "Request Params Required") // 请求参数必需
 | ||||
| 	ErrPermission      = NewError(1104, "Permission Denied")       // 权限不足
 | ||||
| 	ErrJsonUnmarshal   = NewError(1105, "Json Unmarshal Fail")     // JSON反序列化失败
 | ||||
| 	ErrJsonMarshal     = NewError(1106, "Json Marshal Fail")       // JSON序列化失败
 | ||||
| 	ErrInternal        = NewError(1107, "Internal Server Error")   // 内部服务器错误
 | ||||
| 	ErrPassword        = NewError(1108, "Password Incorrect")      // 密码错误
 | ||||
| 	ErrAccountDisabled = NewError(1110, "Account Disabled")        // 账户已禁用
 | ||||
| 	ErrDisabled        = NewError(1111, "Status Disabled")         // 状态已禁用
 | ||||
| 	ErrRecordNotFound  = NewError(1112, "Record Not Found")        // 记录未找到
 | ||||
| 	ErrEmpty           = NewError(110, "Data Is Empty") | ||||
| 	ErrRequestParse    = NewError(111, "Request Parse Fail") | ||||
| 	ErrRequestMust     = NewError(112, "Request Params Required") | ||||
| 	ErrPermission      = NewError(113, "Permission Denied") | ||||
| 	ErrJsonUnmarshal   = NewError(114, "Json Unmarshal Fail") | ||||
| 	ErrJsonMarshal     = NewError(115, "Json Marshal Fail") | ||||
| 	ErrInternal        = NewError(116, "Internal Server Error") | ||||
| 	ErrPassword        = NewError(117, "Password Incorrect") | ||||
| 	ErrAccountNotFound = NewError(118, "Account Not Found") | ||||
| 	ErrAccountDisabled = NewError(119, "Account Disabled") | ||||
| 	ErrDisabled        = NewError(120, "Status Disabled") | ||||
| 	ErrRecordNotFound  = NewError(121, "Record Not Found") | ||||
| ) | ||||
| 
 | ||||
| // Token认证相关错误码,起始码:1300
 | ||||
| // jwt error code ,start:130
 | ||||
| var ( | ||||
| 	ErrTokenAuthNotFound      = NewError(1301, "Token Authorization Not Found")           // Token授权未找到
 | ||||
| 	ErrTokenDataInvalid       = NewError(1302, "Token Authorization Data Invalid")        // Token授权数据无效
 | ||||
| 	ErrTokenBase64Decode      = NewError(1303, "Token Authorization Base64 Decode Error") // Token Base64解码错误
 | ||||
| 	ErrTokenAuthParseFail     = NewError(1304, "Token Authorization Fail")                // Token授权解析失败
 | ||||
| 	ErrTokenAuthKeyId         = NewError(1305, "Token Key:Id Incorrect")                  // Token密钥ID错误
 | ||||
| 	ErrTokenAuthKeyIdentity   = NewError(1306, "Token Key:Identity Incorrect")            // Token密钥身份错误
 | ||||
| 	ErrTokenAuthTokenChanged  = NewError(1307, "Token Authorization Changed")             // Token授权已变更
 | ||||
| 	ErrTokenAuthExpire        = NewError(1308, "Token Authorization Expire")              // Token授权已过期
 | ||||
| 	ErrTokenJsonDecode        = NewError(1309, "Token Authorization JSON Decode Error")   // Token JSON解码错误
 | ||||
| 	ErrTokenJsonEncode        = NewError(1310, "Token Authorization JSON Encode Error")   // Token JSON编码错误
 | ||||
| 	ErrTokenSecretKey         = NewError(1311, "Token SecretKey Error")                   // Token密钥错误
 | ||||
| 	ErrTokenSecretKeyNotFound = NewError(1312, "Token SecretKey Not Found")               // Token密钥未找到
 | ||||
| 	ErrTokenGenerate          = NewError(1313, "Generate Token Fail")                     // 生成令牌失败
 | ||||
| 	ErrTokenParse             = NewError(1314, "Parse Token Fail")                        // 解析令牌失败
 | ||||
| 	ErrJWTAuthNotFound     = NewError(131, "JWT Authorization Not Found") | ||||
| 	ErrJWTBase64Decode     = NewError(132, "JWT Authorization Base64 Decode Error") | ||||
| 	ErrJWTAuthParseFail    = NewError(133, "JWT Authorization Fail") | ||||
| 	ErrJWTAuthKeyId        = NewError(134, "JWT Key:Id Incorrect") | ||||
| 	ErrJWTAuthKeyIdentity  = NewError(135, "JWT Key:Identity Incorrect") | ||||
| 	ErrJWTAuthTokenChanged = NewError(136, "JWT Authorization Changed") | ||||
| 	ErrJWTAuthExpire       = NewError(137, "JWT Authorization Expire") | ||||
| 	ErrJWTJsonDecode       = NewError(138, "JWT Authorization JSON Decode Error") | ||||
| 	ErrJWTJsonEncode       = NewError(139, "JWT Authorization JSON Encode Error") | ||||
| 	ErrJWTSecretKey        = NewError(139, "JWT SecretKey Error") | ||||
| ) | ||||
| 
 | ||||
| // 基础设施相关错误码,起始码:1500
 | ||||
| // model error code ,start:150
 | ||||
| var ( | ||||
| 	ErrDB    = NewError(1501, "DB Fatal Error")    // 数据库致命错误
 | ||||
| 	ErrRedis = NewError(1502, "Redis Fatal Error") // Redis致命错误
 | ||||
| 	ErrMq    = NewError(1503, "MQ Fatal Error")    // 消息队列致命错误
 | ||||
| 	ErrOss   = NewError(1504, "OSS Fatal Error")   // 对象存储致命错误
 | ||||
| 	ErrRpc   = NewError(1505, "RPC Fatal Error")   // RPC致命错误
 | ||||
| 	ErrApm   = NewError(1506, "APM Fatal Error")   // 应用性能监控致命错误
 | ||||
| 	ErrEtcd  = NewError(1507, "Etcd Fatal Error")  // Etcd致命错误
 | ||||
| 	ErrDB    = NewError(151, "DB Fatal Error") | ||||
| 	ErrRedis = NewError(152, "Redis Fatal Error") | ||||
| 	ErrMq    = NewError(153, "MQ Fatal Error") | ||||
| 	ErrOss   = NewError(154, "OSS Fatal Error") | ||||
| 	ErrRpc   = NewError(155, "RPC Fatal Error") | ||||
| 	ErrApm   = NewError(156, "APM Fatal Error") | ||||
| 	ErrEtcd  = NewError(157, "Etcd Fatal Error") | ||||
| ) | ||||
| 
 | ||||
| // Google gRPC标准错误状态码,起始码:1700
 | ||||
| // google grpc error status.
 | ||||
| var ( | ||||
| 	OK                    = NewError(0, "OK")                     // 成功
 | ||||
| 	ErrAccountNotFound    = ErrNotFound(404, "Account")           // 账户未找到
 | ||||
| 	ErrCanceled           = NewError(1702, "Canceled")            // 操作已取消
 | ||||
| 	ErrUnknown            = NewError(1703, "Unknown")             // 未知错误
 | ||||
| 	ErrInvalidArgument    = NewError(1704, "Invalid Argument")    // 无效参数
 | ||||
| 	ErrDeadlineExceeded   = NewError(1705, "Deadline Exceeded")   // 超时
 | ||||
| 	ErrAlreadyExists      = NewError(1706, "Already Exists")      // 已存在
 | ||||
| 	ErrPermissionDenied   = NewError(1707, "Permission Denied")   // 权限拒绝
 | ||||
| 	ErrResourceExhausted  = NewError(1708, "Resource Exhausted")  // 资源耗尽
 | ||||
| 	ErrFailedPrecondition = NewError(1709, "Failed Precondition") // 前置条件失败
 | ||||
| 	ErrAborted            = NewError(1710, "Aborted")             // 操作中止
 | ||||
| 	ErrOutOfRange         = NewError(1711, "Out Of Range")        // 超出范围
 | ||||
| 	ErrUnimplemented      = NewError(1712, "Unimplemented")       // 未实现
 | ||||
| 	ErrUnavailable        = NewError(1713, "Unavailable")         // 不可用
 | ||||
| 	ErrDataLoss           = NewError(1714, "Data Loss")           // 数据丢失
 | ||||
| 	ErrUnauthenticated    = NewError(1715, "Unauthenticated")     // 未认证
 | ||||
| 	OK                    = NewError(171, "OK") | ||||
| 	ErrCanceled           = NewError(172, "Canceled") | ||||
| 	ErrUnknown            = NewError(173, "Unknown") | ||||
| 	ErrInvalidArgument    = NewError(174, "Invalid Argument") | ||||
| 	ErrDeadlineExceeded   = NewError(175, "Deadline Exceeded") | ||||
| 	ErrAlreadyExists      = NewError(176, "Already Exists") | ||||
| 	ErrPermissionDenied   = NewError(177, "Permission Denied") | ||||
| 	ErrResourceExhausted  = NewError(178, "Resource Exhausted") | ||||
| 	ErrFailedPrecondition = NewError(179, "Failed Precondition") | ||||
| 	ErrAborted            = NewError(181, "Aborted") | ||||
| 	ErrOutOfRange         = NewError(182, "Out Of Range") | ||||
| 	ErrUnimplemented      = NewError(183, "Unimplemented") | ||||
| 	ErrUnavailable        = NewError(184, "Unavailable") | ||||
| 	ErrDataLoss           = NewError(185, "Data Loss") | ||||
| 	ErrUnauthenticated    = NewError(186, "Unauthenticated") | ||||
| ) | ||||
| 
 | ||||
| // NewError 创建新的错误实例
 | ||||
| // code: 错误码
 | ||||
| // msg: 错误消息
 | ||||
| func NewError(code int, msg string) error { | ||||
| 	AllErrors[code] = msg | ||||
| 	return status.New(codes.Code(code), msg).Err() | ||||
| } | ||||
| 
 | ||||
| // ErrFatal 创建致命错误,状态码:500
 | ||||
| // code: 错误码
 | ||||
| // msg: 错误消息
 | ||||
| // custom error,status code:500
 | ||||
| func ErrFatal(code int, msg string) error { | ||||
| 	AllErrors[code] = msg | ||||
| 	return status.New(codes.Code(code), msg).Err() | ||||
| } | ||||
| 
 | ||||
| // ErrNotFound 创建未找到错误
 | ||||
| // code: 错误码
 | ||||
| // msg: 错误消息,会自动转换为大写
 | ||||
| func ErrNotFound(code int, msg string) error { | ||||
| 	AllErrors[code] = msg | ||||
| 	return status.New(codes.Code(code), msg).Err() | ||||
| } | ||||
| 
 | ||||
| // IsErr 检查错误是否与指定的错误匹配
 | ||||
| func IsErr(err, target error) bool { | ||||
| 	return status.Code(err) == status.Code(target) | ||||
| } | ||||
| 
 | ||||
| func String(err error, msg string) error { | ||||
| 	return status.New(status.Code(err), err.Error()+", "+msg).Err() | ||||
| } | ||||
|  |  | |||
|  | @ -1,53 +1,37 @@ | |||
| // Package infra 提供基础设施功能
 | ||||
| // 包括统一响应处理、健康检查、日志等
 | ||||
| package infra | ||||
| 
 | ||||
| import ( | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/gin-gonic/gin" | ||||
| 	"google.golang.org/grpc/status" | ||||
| ) | ||||
| 
 | ||||
| var Response Reply | ||||
| 
 | ||||
| // Reply 统一响应结构体
 | ||||
| type Reply struct { | ||||
| 	Code    int32  `json:"code"`    // 响应码
 | ||||
| 	Message string `json:"message"` // 响应消息
 | ||||
| 	Details any    `json:"details"` // 响应数据
 | ||||
| 	Timeseq int64  `json:"timeseq"` // 时间戳序列
 | ||||
| 	Code    int32  `json:"code"` | ||||
| 	Message string `json:"message"` | ||||
| 	Result  any    `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // Success 返回成功响应
 | ||||
| // ctx: Gin上下文
 | ||||
| // data: 响应数据
 | ||||
| func (reply *Reply) Success(ctx *gin.Context, data any) { | ||||
| 	reply.Code = 0 | ||||
| 	reply.Details = data | ||||
| 	reply.Result = data | ||||
| 	reply.Message = "" | ||||
| 	reply.Timeseq = time.Now().UnixMilli() | ||||
| 	if data == nil { | ||||
| 		reply.Details = "" | ||||
| 		reply.Result = "" | ||||
| 	} | ||||
| 	ctx.JSON(200, reply) | ||||
| } | ||||
| 
 | ||||
| // Error 返回错误响应
 | ||||
| // ctx: Gin上下文
 | ||||
| // err: 错误对象
 | ||||
| func (reply *Reply) Error(ctx *gin.Context, err error) { | ||||
| 	reply.Code = 500 | ||||
| 	reply.Details = "" | ||||
| 	// 默认状态码为500
 | ||||
| 	reply.Result = "" | ||||
| 	// Status code defaults to 500
 | ||||
| 	e, ok := status.FromError(err) | ||||
| 	if ok { | ||||
| 		reply.Code = int32(e.Code()) | ||||
| 		reply.Message = e.Message() | ||||
| 	} else { | ||||
| 		reply.Message = err.Error() | ||||
| 	} | ||||
| 	reply.Message = e.Message() | ||||
| 
 | ||||
| 	// 发送错误响应
 | ||||
| 	// Send error
 | ||||
| 	ctx.JSON(200, reply) | ||||
| } | ||||
|  |  | |||
|  | @ -5,7 +5,7 @@ import ( | |||
| 	"log" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"git.apinb.com/bsm-sdk/core/printer" | ||||
| 	"git.apinb.com/bsm-sdk/core/print" | ||||
| 	"git.apinb.com/bsm-sdk/core/utils" | ||||
| 	clientv3 "go.etcd.io/etcd/client/v3" | ||||
| ) | ||||
|  | @ -37,10 +37,10 @@ func (s *service) Register(cli *clientv3.Client, serviceName string, port string | |||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	printer.Info("[BSM Register] Service Key: %s", key) | ||||
| 	printer.Info("[BSM Register] Service Val: %s", serviceAddr) | ||||
| 	print.Info("[BSM Register] Service Key: %s", key) | ||||
| 	print.Info("[BSM Register] Service Val: %s", serviceAddr) | ||||
| 
 | ||||
| 	printer.Success("[BSM Register] Service Register Complete.") | ||||
| 	print.Success("[BSM Register] Service Register Complete.") | ||||
| 
 | ||||
| 	go func() { | ||||
| 		for keepAliveResp := range keepAliveChan { | ||||
|  |  | |||
							
								
								
									
										422
									
								
								logger/logger.go
								
								
								
								
							
							
						
						
									
										422
									
								
								logger/logger.go
								
								
								
								
							|  | @ -1,422 +0,0 @@ | |||
| package logger | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"log" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"git.apinb.com/bsm-sdk/core/conf" | ||||
| 	"git.apinb.com/bsm-sdk/core/utils" | ||||
| 	"git.apinb.com/bsm-sdk/core/vars" | ||||
| ) | ||||
| 
 | ||||
| // Logger 日志器结构
 | ||||
| type Logger struct { | ||||
| 	level       vars.LogLevel | ||||
| 	infoLogger  *log.Logger | ||||
| 	warnLogger  *log.Logger | ||||
| 	errorLogger *log.Logger | ||||
| 	fatalLogger *log.Logger | ||||
| 	debugLogger *log.Logger | ||||
| 
 | ||||
| 	fileWriter    io.Writer | ||||
| 	consoleWriter io.Writer | ||||
| 
 | ||||
| 	mu          sync.RWMutex | ||||
| 	name        string | ||||
| 	logDir      string | ||||
| 	currentDate string | ||||
| 	onRemote    bool | ||||
| 	endpoint    string | ||||
| } | ||||
| 
 | ||||
| var ( | ||||
| 	globalLogger *Logger | ||||
| 	once         sync.Once | ||||
| ) | ||||
| 
 | ||||
| // 初始化Logger配置
 | ||||
| func New(cfg *conf.LogConf) { | ||||
| 	if cfg == nil { | ||||
| 		cfg = &conf.LogConf{ | ||||
| 			Name:     strings.ToLower(vars.ServiceKey), | ||||
| 			Level:    vars.LogLevel(vars.DEBUG), | ||||
| 			Dir:      "./logs/", | ||||
| 			Endpoint: "", | ||||
| 			Console:  true, | ||||
| 			File:     true, | ||||
| 			Remote:   false, | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	InitLogger(cfg) | ||||
| } | ||||
| 
 | ||||
| // InitLogger 初始化全局日志器
 | ||||
| func InitLogger(cfg *conf.LogConf) error { | ||||
| 	var err error | ||||
| 	once.Do(func() { | ||||
| 		globalLogger, err = NewLogger(cfg) | ||||
| 	}) | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| // NewLogger 创建新的日志器
 | ||||
| func NewLogger(cfg *conf.LogConf) (*Logger, error) { | ||||
| 	// 确保日志目录存在
 | ||||
| 	if err := os.MkdirAll(cfg.Dir, 0755); err != nil { | ||||
| 		return nil, fmt.Errorf("创建日志目录失败: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	// 控制台输出
 | ||||
| 	consoleWriter := os.Stdout | ||||
| 
 | ||||
| 	// 文件输出
 | ||||
| 	fileWriter, err := createLogFile(cfg.Dir, cfg.Name) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("创建日志文件失败: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	// 创建多输出写入器
 | ||||
| 	multiWriter := io.MultiWriter(consoleWriter, fileWriter) | ||||
| 
 | ||||
| 	logger := &Logger{ | ||||
| 		level:         cfg.Level, | ||||
| 		fileWriter:    fileWriter, | ||||
| 		consoleWriter: consoleWriter, | ||||
| 		logDir:        cfg.Dir, | ||||
| 		name:          strings.ToLower(cfg.Name), | ||||
| 		currentDate:   time.Now().Format("2006-01-02"), | ||||
| 		onRemote:      cfg.Remote, | ||||
| 		endpoint:      cfg.Endpoint, | ||||
| 	} | ||||
| 
 | ||||
| 	// 创建不同级别的日志器
 | ||||
| 	logger.infoLogger = log.New(multiWriter, "[INFO] ", log.LstdFlags) | ||||
| 	logger.warnLogger = log.New(multiWriter, "[WARN] ", log.LstdFlags) | ||||
| 	logger.errorLogger = log.New(multiWriter, "[ERROR] ", log.LstdFlags) | ||||
| 	logger.fatalLogger = log.New(multiWriter, "[FATAL] ", log.LstdFlags) | ||||
| 	logger.debugLogger = log.New(multiWriter, "[DEBUG] ", log.LstdFlags) | ||||
| 
 | ||||
| 	return logger, nil | ||||
| } | ||||
| 
 | ||||
| // createLogFile 创建日志文件
 | ||||
| func createLogFile(logDir, name string) (io.Writer, error) { | ||||
| 	filename := fmt.Sprintf("%s_%s.log", name, time.Now().Format("2006-01-02")) | ||||
| 	filepath := filepath.Join(logDir, filename) | ||||
| 
 | ||||
| 	file, err := os.OpenFile(filepath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return file, nil | ||||
| } | ||||
| 
 | ||||
| // checkAndRotateLog 检查并轮转日志文件
 | ||||
| func (l *Logger) checkAndRotateLog() error { | ||||
| 	today := time.Now().Format("2006-01-02") | ||||
| 	if l.currentDate != today { | ||||
| 		l.mu.Lock() | ||||
| 		defer l.mu.Unlock() | ||||
| 
 | ||||
| 		if l.currentDate != today { | ||||
| 			// 关闭旧文件
 | ||||
| 			if closer, ok := l.fileWriter.(io.Closer); ok { | ||||
| 				closer.Close() | ||||
| 			} | ||||
| 
 | ||||
| 			// 创建新文件
 | ||||
| 			newFileWriter, err := createLogFile(l.logDir, l.name) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 
 | ||||
| 			l.fileWriter = newFileWriter | ||||
| 			l.currentDate = today | ||||
| 
 | ||||
| 			// 重新创建多输出写入器
 | ||||
| 			multiWriter := io.MultiWriter(l.consoleWriter, l.fileWriter) | ||||
| 
 | ||||
| 			l.infoLogger = log.New(multiWriter, "[INFO] ", log.LstdFlags) | ||||
| 			l.warnLogger = log.New(multiWriter, "[WARN] ", log.LstdFlags) | ||||
| 			l.errorLogger = log.New(multiWriter, "[ERROR] ", log.LstdFlags) | ||||
| 			l.fatalLogger = log.New(multiWriter, "[FATAL] ", log.LstdFlags) | ||||
| 			l.debugLogger = log.New(multiWriter, "[DEBUG] ", log.LstdFlags) | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (l *Logger) sendToRemote(level, name, out string) { | ||||
| 	if l.endpoint == "" { | ||||
| 		return | ||||
| 	} | ||||
| 	data := map[string]interface{}{ | ||||
| 		"level": level, | ||||
| 		"name":  name, | ||||
| 		"out":   out, | ||||
| 	} | ||||
| 	jsonBytes, _ := json.Marshal(data) | ||||
| 	utils.HttpPost(l.endpoint, nil, jsonBytes) | ||||
| } | ||||
| 
 | ||||
| // Debug 输出调试信息
 | ||||
| func (l *Logger) Debug(v ...interface{}) { | ||||
| 	if l.level <= vars.DEBUG { | ||||
| 		l.checkAndRotateLog() | ||||
| 		out := fmt.Sprint(v...) | ||||
| 		if l.onRemote { | ||||
| 			go l.sendToRemote("DEBUG", l.name, out) | ||||
| 		} | ||||
| 		l.debugLogger.Output(2, out) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Debugf 格式化输出调试信息
 | ||||
| func (l *Logger) Debugf(format string, v ...interface{}) { | ||||
| 	if l.level <= vars.DEBUG { | ||||
| 		l.checkAndRotateLog() | ||||
| 		out := fmt.Sprintf(format, v...) | ||||
| 		if l.onRemote { | ||||
| 			go l.sendToRemote("DEBUG", l.name, out) | ||||
| 		} | ||||
| 		l.debugLogger.Output(2, out) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Info 输出信息
 | ||||
| func (l *Logger) Info(v ...interface{}) { | ||||
| 	if l.level <= vars.INFO { | ||||
| 		l.checkAndRotateLog() | ||||
| 		out := fmt.Sprint(v...) | ||||
| 		if l.onRemote { | ||||
| 			go l.sendToRemote("INFO", l.name, out) | ||||
| 		} | ||||
| 		l.infoLogger.Output(2, out) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Infof 格式化输出信息
 | ||||
| func (l *Logger) Infof(format string, v ...interface{}) { | ||||
| 	if l.level <= vars.INFO { | ||||
| 		l.checkAndRotateLog() | ||||
| 		out := fmt.Sprintf(format, v...) | ||||
| 		if l.onRemote { | ||||
| 			go l.sendToRemote("INFO", l.name, out) | ||||
| 		} | ||||
| 		l.infoLogger.Output(2, out) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Warn 输出警告
 | ||||
| func (l *Logger) Warn(v ...interface{}) { | ||||
| 	if l.level <= vars.WARN { | ||||
| 		l.checkAndRotateLog() | ||||
| 		out := fmt.Sprint(v...) | ||||
| 		if l.onRemote { | ||||
| 			go l.sendToRemote("WARN", l.name, out) | ||||
| 		} | ||||
| 		l.warnLogger.Output(2, out) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Warnf 格式化输出警告
 | ||||
| func (l *Logger) Warnf(format string, v ...interface{}) { | ||||
| 	if l.level <= vars.WARN { | ||||
| 		l.checkAndRotateLog() | ||||
| 		out := fmt.Sprintf(format, v...) | ||||
| 		if l.onRemote { | ||||
| 			go l.sendToRemote("WARN", l.name, out) | ||||
| 		} | ||||
| 		l.warnLogger.Output(2, out) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Error 输出错误
 | ||||
| func (l *Logger) Error(v ...interface{}) { | ||||
| 	if l.level <= vars.ERROR { | ||||
| 		l.checkAndRotateLog() | ||||
| 		out := fmt.Sprint(v...) | ||||
| 		if l.onRemote { | ||||
| 			go l.sendToRemote("ERROR", l.name, out) | ||||
| 		} | ||||
| 		l.errorLogger.Output(2, out) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Errorf 格式化输出错误
 | ||||
| func (l *Logger) Errorf(format string, v ...interface{}) { | ||||
| 	if l.level <= vars.ERROR { | ||||
| 		l.checkAndRotateLog() | ||||
| 		out := fmt.Sprintf(format, v...) | ||||
| 		if l.onRemote { | ||||
| 			go l.sendToRemote("ERROR", l.name, out) | ||||
| 		} | ||||
| 		l.errorLogger.Output(2, out) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Fatal 输出致命错误并退出程序
 | ||||
| func (l *Logger) Fatal(v ...interface{}) { | ||||
| 	l.checkAndRotateLog() | ||||
| 	out := fmt.Sprint(v...) | ||||
| 	if l.onRemote { | ||||
| 		go l.sendToRemote("FATAL", l.name, out) | ||||
| 	} | ||||
| 	l.fatalLogger.Output(2, out) | ||||
| 	os.Exit(1) | ||||
| } | ||||
| 
 | ||||
| // Fatalf 格式化输出致命错误并退出程序
 | ||||
| func (l *Logger) Fatalf(format string, v ...interface{}) { | ||||
| 	l.checkAndRotateLog() | ||||
| 	out := fmt.Sprintf(format, v...) | ||||
| 	if l.onRemote { | ||||
| 		go l.sendToRemote("FATAL", l.name, out) | ||||
| 	} | ||||
| 	l.fatalLogger.Output(2, out) | ||||
| 	os.Exit(1) | ||||
| } | ||||
| 
 | ||||
| // Print 输出信息(兼容标准log包)
 | ||||
| func (l *Logger) Print(v ...interface{}) { | ||||
| 	l.Info(v...) | ||||
| } | ||||
| 
 | ||||
| // Printf 格式化输出信息(兼容标准log包)
 | ||||
| func (l *Logger) Printf(format string, v ...interface{}) { | ||||
| 	l.Infof(format, v...) | ||||
| } | ||||
| 
 | ||||
| // Println 输出信息并换行(兼容标准log包)
 | ||||
| func (l *Logger) Println(v ...interface{}) { | ||||
| 	l.Info(v...) | ||||
| } | ||||
| 
 | ||||
| // SetLevel 设置日志级别
 | ||||
| func (l *Logger) SetLevel(level vars.LogLevel) { | ||||
| 	l.mu.Lock() | ||||
| 	defer l.mu.Unlock() | ||||
| 	l.level = level | ||||
| } | ||||
| 
 | ||||
| // GetLevel 获取日志级别
 | ||||
| func (l *Logger) GetLevel() vars.LogLevel { | ||||
| 	l.mu.RLock() | ||||
| 	defer l.mu.RUnlock() | ||||
| 	return l.level | ||||
| } | ||||
| 
 | ||||
| // Close 关闭日志器
 | ||||
| func (l *Logger) Close() error { | ||||
| 	if closer, ok := l.fileWriter.(io.Closer); ok { | ||||
| 		return closer.Close() | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // 全局日志函数(兼容标准log包)
 | ||||
| 
 | ||||
| // Debug 全局调试日志
 | ||||
| func Debug(v ...interface{}) { | ||||
| 	if globalLogger != nil { | ||||
| 		globalLogger.Debug(v...) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Debugf 全局调试日志
 | ||||
| func Debugf(format string, v ...interface{}) { | ||||
| 	if globalLogger != nil { | ||||
| 		globalLogger.Debugf(format, v...) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Info 全局信息日志
 | ||||
| func Info(v ...interface{}) { | ||||
| 	if globalLogger != nil { | ||||
| 		globalLogger.Info(v...) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Infof 全局信息日志
 | ||||
| func Infof(format string, v ...interface{}) { | ||||
| 	if globalLogger != nil { | ||||
| 		globalLogger.Infof(format, v...) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Warn 全局警告日志
 | ||||
| func Warn(v ...interface{}) { | ||||
| 	if globalLogger != nil { | ||||
| 		globalLogger.Warn(v...) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Warnf 全局警告日志
 | ||||
| func Warnf(format string, v ...interface{}) { | ||||
| 	if globalLogger != nil { | ||||
| 		globalLogger.Warnf(format, v...) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Error 全局错误日志
 | ||||
| func Error(v ...interface{}) { | ||||
| 	if globalLogger != nil { | ||||
| 		globalLogger.Error(v...) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Errorf 全局错误日志
 | ||||
| func Errorf(format string, v ...interface{}) { | ||||
| 	if globalLogger != nil { | ||||
| 		globalLogger.Errorf(format, v...) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Fatal 全局致命错误日志
 | ||||
| func Fatal(v ...interface{}) { | ||||
| 	if globalLogger != nil { | ||||
| 		globalLogger.Fatal(v...) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Fatalf 全局致命错误日志
 | ||||
| func Fatalf(format string, v ...interface{}) { | ||||
| 	if globalLogger != nil { | ||||
| 		globalLogger.Fatalf(format, v...) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Print 全局打印日志(兼容标准log包)
 | ||||
| func Print(v ...interface{}) { | ||||
| 	if globalLogger != nil { | ||||
| 		globalLogger.Print(v...) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Printf 全局打印日志(兼容标准log包)
 | ||||
| func Printf(format string, v ...interface{}) { | ||||
| 	if globalLogger != nil { | ||||
| 		globalLogger.Printf(format, v...) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Println 全局打印日志(兼容标准log包)
 | ||||
| func Println(v ...interface{}) { | ||||
| 	if globalLogger != nil { | ||||
| 		globalLogger.Println(v...) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // GetLogger 获取全局日志器实例
 | ||||
| func GetLogger() *Logger { | ||||
| 	return globalLogger | ||||
| } | ||||
|  | @ -1,22 +1,17 @@ | |||
| // Package middleware 提供HTTP中间件功能
 | ||||
| // 包括JWT认证、CORS、运行模式等中间件
 | ||||
| package middleware | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"log" | ||||
| 	"net/http" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"git.apinb.com/bsm-sdk/core/crypto/token" | ||||
| 	"git.apinb.com/bsm-sdk/core/env" | ||||
| 	"git.apinb.com/bsm-sdk/core/crypto/encipher" | ||||
| 	"git.apinb.com/bsm-sdk/core/errcode" | ||||
| 	"git.apinb.com/bsm-sdk/core/types" | ||||
| 	"github.com/gin-gonic/gin" | ||||
| ) | ||||
| 
 | ||||
| // JwtAuth JWT认证中间件
 | ||||
| // time_verify: 是否验证token过期时间
 | ||||
| // 返回: Gin中间件函数
 | ||||
| func JwtAuth(time_verify bool) gin.HandlerFunc { | ||||
| 	return func(c *gin.Context) { | ||||
| 		// 从请求头中获取 Authorization
 | ||||
|  | @ -27,27 +22,8 @@ func JwtAuth(time_verify bool) gin.HandlerFunc { | |||
| 			c.Abort() | ||||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		// 检测是否需要验证token时间
 | ||||
| 		if time_verify { | ||||
| 			// 判断时间claims.ExpiresAt
 | ||||
| 			isExpire, err := token.New(env.Runtime.JwtSecretKey).IsExpired(authHeader) | ||||
| 			if err != nil { | ||||
| 				log.Println("token解析异常:", err) | ||||
| 				c.JSON(http.StatusUnauthorized, gin.H{"error": "Token is required"}) | ||||
| 				c.Abort() | ||||
| 				return | ||||
| 			} | ||||
| 			if isExpire { | ||||
| 				log.Println("token过期,请重新获取:", "Token has expired") | ||||
| 				c.JSON(http.StatusUnauthorized, gin.H{"error": "Token has expired"}) | ||||
| 				c.Abort() | ||||
| 				return | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		// 提取Token
 | ||||
| 		claims, err := token.New(env.Runtime.JwtSecretKey).ParseJwt(authHeader) | ||||
| 		claims, err := encipher.ParseTokenAes(authHeader) | ||||
| 		if err != nil || claims == nil { | ||||
| 			log.Printf("提取token异常:%v\n", err) | ||||
| 			c.JSON(http.StatusUnauthorized, gin.H{"error": "Token is required"}) | ||||
|  | @ -55,6 +31,17 @@ func JwtAuth(time_verify bool) gin.HandlerFunc { | |||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		// 检测是否需要验证token时间
 | ||||
| 		if time_verify { | ||||
| 			// 判断时间claims.ExpiresAt
 | ||||
| 			if time.Now().Unix() > claims.ExpiresAt { | ||||
| 				log.Println("token过期,请重新获取:", "Token has expired") | ||||
| 				c.JSON(http.StatusUnauthorized, gin.H{"error": "Token has expired"}) | ||||
| 				c.Abort() | ||||
| 				return | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		// 将解析后的 Token 存储到上下文中
 | ||||
| 		c.Set("Auth", claims) | ||||
| 		// 如果 Token 有效,继续处理请求
 | ||||
|  | @ -62,14 +49,12 @@ func JwtAuth(time_verify bool) gin.HandlerFunc { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| // ParseAuth 获取上下文用户登录信息
 | ||||
| // c: Gin上下文
 | ||||
| // 返回: JWT声明信息
 | ||||
| // 获取上下文用户登录信息
 | ||||
| func ParseAuth(c *gin.Context) (*types.JwtClaims, error) { | ||||
| 	claims, ok := c.Get("Auth") | ||||
| 	if !ok { | ||||
| 		log.Printf("获取登录信息异常: %v", errcode.ErrTokenAuthNotFound) | ||||
| 		return nil, errcode.ErrTokenAuthNotFound | ||||
| 		log.Printf("获取登录信息异常: %v", errcode.ErrJWTAuthNotFound) | ||||
| 		return nil, errcode.ErrJWTAuthNotFound | ||||
| 	} | ||||
| 
 | ||||
| 	json_claims, err := json.Marshal(claims) | ||||
|  |  | |||
|  | @ -0,0 +1,14 @@ | |||
| package oplog | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 
 | ||||
| 	"git.apinb.com/bsm-sdk/core/utils" | ||||
| ) | ||||
| 
 | ||||
| func New(endpoint string, data []*LogItem) { | ||||
| 	jsonBytes, _ := json.Marshal(data) | ||||
| 
 | ||||
| 	go utils.HttpPost(endpoint, nil, jsonBytes) | ||||
| 
 | ||||
| } | ||||
|  | @ -1,10 +1,4 @@ | |||
| package infra | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 
 | ||||
| 	"git.apinb.com/bsm-sdk/core/utils" | ||||
| ) | ||||
| package oplog | ||||
| 
 | ||||
| type LogItem struct { | ||||
| 	OpID   uint   `json:"op_id"` | ||||
|  | @ -28,10 +22,3 @@ var ( | |||
| 	Type_Other    string = "other" | ||||
| 	Type_Create   string = "create" | ||||
| ) | ||||
| 
 | ||||
| func PushLog(endpoint string, data []*LogItem) { | ||||
| 	jsonBytes, _ := json.Marshal(data) | ||||
| 
 | ||||
| 	go utils.HttpPost(endpoint, nil, jsonBytes) | ||||
| 
 | ||||
| } | ||||
|  | @ -1,8 +1,6 @@ | |||
| package printer | ||||
| package print | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"log" | ||||
| 	"os" | ||||
|  | @ -38,11 +36,3 @@ func Error(format string, a ...interface{}) { | |||
| 	message := fmt.Sprintf("\033[31m[Error] "+format+"\033[0m\n", a...) | ||||
| 	logger.Print(message) | ||||
| } | ||||
| 
 | ||||
| func Json(v any) { | ||||
| 	jsonBy, _ := json.Marshal(v) | ||||
| 	var out bytes.Buffer | ||||
| 	json.Indent(&out, jsonBy, "", "\t") | ||||
| 	out.WriteTo(os.Stdout) | ||||
| 	fmt.Printf("\n") | ||||
| } | ||||
|  | @ -1,204 +0,0 @@ | |||
| package service | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"net" | ||||
| 	"net/url" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"git.apinb.com/bsm-sdk/core/utils" | ||||
| ) | ||||
| 
 | ||||
| type NetworkAddress struct { | ||||
| 	Protocol string // tcp, tcp4, tcp6, unix, unixpacket
 | ||||
| 	Host     string // IP 地址或主机名
 | ||||
| 	Port     string // 端口号
 | ||||
| 	Path     string // Unix socket 路径
 | ||||
| 	Raw      string // 原始字符串
 | ||||
| } | ||||
| 
 | ||||
| // 解析网络地址字符串
 | ||||
| // "tcp://0.0.0.0:1212",
 | ||||
| //
 | ||||
| //	"tcp4://127.0.0.1:8080",
 | ||||
| //	"tcp6://[::1]:8080",
 | ||||
| //	"unix:///data/app/passport.sock",
 | ||||
| //	"unixpacket:///tmp/mysql.sock",
 | ||||
| //	":8080",                    // 传统格式
 | ||||
| //	"/tmp/server.sock",         // 传统Unix格式
 | ||||
| //	"invalid://address",        // 错误格式
 | ||||
| func ParseNetworkAddress(addr string) (*NetworkAddress, error) { | ||||
| 	// 如果包含 ://,按 URL 解析
 | ||||
| 	if strings.Contains(addr, "://") { | ||||
| 		return parseURLStyle(addr) | ||||
| 	} | ||||
| 
 | ||||
| 	// 否则按传统格式解析
 | ||||
| 	return parseTraditionalStyle(addr) | ||||
| } | ||||
| 
 | ||||
| // 解析 tcp://0.0.0.0:1212 或 unix:///path/to/socket 格式
 | ||||
| func parseURLStyle(addr string) (*NetworkAddress, error) { | ||||
| 	u, err := url.Parse(addr) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("解析URL失败: %w", err) | ||||
| 	} | ||||
| 
 | ||||
| 	result := &NetworkAddress{ | ||||
| 		Protocol: u.Scheme, | ||||
| 		Raw:      addr, | ||||
| 	} | ||||
| 
 | ||||
| 	switch u.Scheme { | ||||
| 	case "tcp", "tcp4", "tcp6": | ||||
| 		return parseTCPURL(u, result) | ||||
| 	case "unix", "unixpacket": | ||||
| 		return parseUnixURL(u, result) | ||||
| 	default: | ||||
| 		return nil, fmt.Errorf("不支持的协议: %s", u.Scheme) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // 解析 TCP 类型的 URL
 | ||||
| func parseTCPURL(u *url.URL, result *NetworkAddress) (*NetworkAddress, error) { | ||||
| 	host, port, err := net.SplitHostPort(u.Host) | ||||
| 	if err != nil { | ||||
| 		// 如果没有端口,尝试添加默认端口
 | ||||
| 		if strings.Contains(err.Error(), "missing port") { | ||||
| 			host = u.Host | ||||
| 			port = "0" // 默认端口
 | ||||
| 		} else { | ||||
| 			return nil, fmt.Errorf("解析TCP地址失败: %w", err) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	result.Host = host | ||||
| 	result.Port = port | ||||
| 
 | ||||
| 	// 根据主机地址确定具体的协议类型
 | ||||
| 	if result.Protocol == "tcp" { | ||||
| 		result.Protocol = determineTCPProtocol(host) | ||||
| 	} | ||||
| 
 | ||||
| 	return result, nil | ||||
| } | ||||
| 
 | ||||
| // 解析 Unix socket 类型的 URL
 | ||||
| func parseUnixURL(u *url.URL, result *NetworkAddress) (*NetworkAddress, error) { | ||||
| 	// Unix socket 路径在 URL 的 Path 字段
 | ||||
| 	if u.Path == "" { | ||||
| 		return nil, fmt.Errorf("Unix socket 路径不能为空") | ||||
| 	} | ||||
| 
 | ||||
| 	result.Path = u.Path | ||||
| 
 | ||||
| 	// 如果协议是 unix,但路径表明需要数据包传输,可以自动升级
 | ||||
| 	if result.Protocol == "unix" && strings.Contains(u.Path, "packet") { | ||||
| 		result.Protocol = "unixpacket" | ||||
| 	} | ||||
| 
 | ||||
| 	return result, nil | ||||
| } | ||||
| 
 | ||||
| // 根据主机地址确定 TCP 协议类型
 | ||||
| func determineTCPProtocol(host string) string { | ||||
| 	if host == "" { | ||||
| 		return "tcp" // 默认
 | ||||
| 	} | ||||
| 
 | ||||
| 	// 解析 IP 地址
 | ||||
| 	ip := net.ParseIP(host) | ||||
| 	if ip != nil { | ||||
| 		if ip.To4() != nil { | ||||
| 			return "tcp4" | ||||
| 		} | ||||
| 		return "tcp6" | ||||
| 	} | ||||
| 
 | ||||
| 	// 如果是特殊地址
 | ||||
| 	switch host { | ||||
| 	case "0.0.0.0", "127.0.0.1", "localhost": | ||||
| 		return "tcp4" | ||||
| 	case "::", "::1": | ||||
| 		return "tcp6" | ||||
| 	default: | ||||
| 		return "tcp" // 默认支持双栈
 | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // 解析传统格式如 ":8080", "127.0.0.1:8080", "/tmp/socket"
 | ||||
| func parseTraditionalStyle(addr string) (*NetworkAddress, error) { | ||||
| 	// 检查是否是 Unix socket(包含路径分隔符)
 | ||||
| 	if strings.Contains(addr, "/") || strings.HasPrefix(addr, "@/") { | ||||
| 		return &NetworkAddress{Protocol: "unix", Path: addr}, nil | ||||
| 	} | ||||
| 
 | ||||
| 	// 否则按 TCP 地址解析
 | ||||
| 	host, port, err := net.SplitHostPort(addr) | ||||
| 	if err == nil { | ||||
| 		return &NetworkAddress{Protocol: "tcp", Host: host, Port: port}, nil | ||||
| 	} | ||||
| 
 | ||||
| 	// 检查是否是端口号
 | ||||
| 	if ok := utils.IsNumber(addr); ok { | ||||
| 		return &NetworkAddress{Protocol: "tcp", Host: "0.0.0.0", Port: addr}, nil | ||||
| 	} | ||||
| 
 | ||||
| 	return nil, fmt.Errorf("解析地址失败: %w", err) | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| // 获取网络类型用于 net.Dial 或 net.Listen
 | ||||
| func (na *NetworkAddress) Network() string { | ||||
| 	return na.Protocol | ||||
| } | ||||
| 
 | ||||
| // 获取地址字符串用于 net.Dial 或 net.Listen
 | ||||
| func (na *NetworkAddress) Address() string { | ||||
| 	switch na.Protocol { | ||||
| 	case "tcp", "tcp4", "tcp6": | ||||
| 		if na.Port == "" { | ||||
| 			return na.Host | ||||
| 		} | ||||
| 		return net.JoinHostPort(na.Host, na.Port) | ||||
| 	case "unix", "unixpacket": | ||||
| 		return na.Path | ||||
| 	default: | ||||
| 		return na.Raw | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // 格式化输出
 | ||||
| func (na *NetworkAddress) String() string { | ||||
| 	switch na.Protocol { | ||||
| 	case "tcp", "tcp4", "tcp6": | ||||
| 		return fmt.Sprintf("%s://%s", na.Protocol, net.JoinHostPort(na.Host, na.Port)) | ||||
| 	case "unix", "unixpacket": | ||||
| 		return fmt.Sprintf("%s://%s", na.Protocol, na.Path) | ||||
| 	default: | ||||
| 		return na.Raw | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // 验证地址是否有效
 | ||||
| func (na *NetworkAddress) Validate() error { | ||||
| 	switch na.Protocol { | ||||
| 	case "tcp", "tcp4", "tcp6": | ||||
| 		if na.Host == "" && na.Port == "" { | ||||
| 			return fmt.Errorf("TCP地址需要主机和端口") | ||||
| 		} | ||||
| 		// 验证端口
 | ||||
| 		if na.Port != "" { | ||||
| 			if _, err := net.LookupPort("tcp", na.Port); err != nil { | ||||
| 				return fmt.Errorf("无效的端口: %s", na.Port) | ||||
| 			} | ||||
| 		} | ||||
| 	case "unix", "unixpacket": | ||||
| 		if na.Path == "" { | ||||
| 			return fmt.Errorf("Unix socket路径不能为空") | ||||
| 		} | ||||
| 	default: | ||||
| 		return fmt.Errorf("不支持的协议: %s", na.Protocol) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | @ -3,9 +3,9 @@ package service | |||
| import ( | ||||
| 	"context" | ||||
| 
 | ||||
| 	"git.apinb.com/bsm-sdk/core/crypto/token" | ||||
| 	"git.apinb.com/bsm-sdk/core/env" | ||||
| 	"git.apinb.com/bsm-sdk/core/crypto/encipher" | ||||
| 	"git.apinb.com/bsm-sdk/core/errcode" | ||||
| 	"git.apinb.com/bsm-sdk/core/types" | ||||
| 	"git.apinb.com/bsm-sdk/core/utils" | ||||
| 	"google.golang.org/grpc/metadata" | ||||
| ) | ||||
|  | @ -16,19 +16,19 @@ type ParseOptions struct { | |||
| 	MustPrivateAllow bool   // 是否只允许私有IP访问
 | ||||
| } | ||||
| 
 | ||||
| func ParseMetaCtx(ctx context.Context, opts *ParseOptions) (*token.Claims, error) { | ||||
| func ParseMetaCtx(ctx context.Context, opts *ParseOptions) (*types.JwtClaims, error) { | ||||
| 	// 解析metada中的信息并验证
 | ||||
| 	md, ok := metadata.FromIncomingContext(ctx) | ||||
| 	if !ok { | ||||
| 		return nil, errcode.ErrTokenAuthNotFound | ||||
| 		return nil, errcode.ErrJWTAuthNotFound | ||||
| 	} | ||||
| 
 | ||||
| 	var Authorizations []string = md.Get("authorization") | ||||
| 	if len(Authorizations) == 0 || Authorizations[0] == "" { | ||||
| 		return nil, errcode.ErrTokenAuthNotFound | ||||
| 		return nil, errcode.ErrJWTAuthNotFound | ||||
| 	} | ||||
| 
 | ||||
| 	claims, err := token.New(env.Runtime.JwtSecretKey).ParseJwt(Authorizations[0]) | ||||
| 	claims, err := encipher.ParseTokenAes(Authorizations[0]) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | @ -48,7 +48,7 @@ func ParseMetaCtx(ctx context.Context, opts *ParseOptions) (*token.Claims, error | |||
| 
 | ||||
| } | ||||
| 
 | ||||
| func checkRole(claims *token.Claims, roleKey, roleValue string) bool { | ||||
| func checkRole(claims *types.JwtClaims, roleKey, roleValue string) bool { | ||||
| 	if roleValue == "" { | ||||
| 		return true | ||||
| 	} | ||||
|  |  | |||
|  | @ -5,7 +5,7 @@ import ( | |||
| 	"strings" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"git.apinb.com/bsm-sdk/core/printer" | ||||
| 	"git.apinb.com/bsm-sdk/core/print" | ||||
| 	"git.apinb.com/bsm-sdk/core/vars" | ||||
| 	clientv3 "go.etcd.io/etcd/client/v3" | ||||
| ) | ||||
|  | @ -94,11 +94,7 @@ func (s *ServiceRegister) Close() error { | |||
| 
 | ||||
| func (s *ServiceRegister) SetAnonymous(key string, urls []string) { | ||||
| 	// remove reppeat, clear service all anonymous uri.
 | ||||
| 	anonymous, err := s.cli.Get(context.Background(), key) | ||||
| 	if err != nil { | ||||
| 		printer.Error("[BSM Register] Get Anonymous Fail: %v", err) | ||||
| 		return | ||||
| 	} | ||||
| 	anonymous, _ := s.cli.Get(context.Background(), key) | ||||
| 
 | ||||
| 	var as []string | ||||
| 	if len(anonymous.Kvs) > 0 { | ||||
|  | @ -110,12 +106,12 @@ func (s *ServiceRegister) SetAnonymous(key string, urls []string) { | |||
| 	newAnonymous := strings.Join(as, ",") | ||||
| 
 | ||||
| 	// put anonymous to etcd
 | ||||
| 	_, err = s.cli.Put(context.Background(), key, newAnonymous) | ||||
| 	_, err := s.cli.Put(context.Background(), key, newAnonymous) | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		printer.Error("[BSM Register] Anonymous Fail.") | ||||
| 		print.Error("[BSM Register] Anonymous Fail.") | ||||
| 	} else { | ||||
| 		printer.Info("[BSM Register] Anonymous: %s", newAnonymous) | ||||
| 		print.Info("[BSM Register] Anonymous: %s", newAnonymous) | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -1,5 +1,3 @@ | |||
| // Package service 提供微服务管理功能
 | ||||
| // 包括服务启动、注册、网关代理等核心功能
 | ||||
| package service | ||||
| 
 | ||||
| import ( | ||||
|  | @ -16,66 +14,56 @@ import ( | |||
| 
 | ||||
| 	"git.apinb.com/bsm-sdk/core/conf" | ||||
| 	"git.apinb.com/bsm-sdk/core/env" | ||||
| 	"git.apinb.com/bsm-sdk/core/printer" | ||||
| 	"git.apinb.com/bsm-sdk/core/print" | ||||
| 	"git.apinb.com/bsm-sdk/core/vars" | ||||
| 	clientv3 "go.etcd.io/etcd/client/v3" | ||||
| 	"google.golang.org/grpc" | ||||
| ) | ||||
| 
 | ||||
| type ( | ||||
| 	// RegisterFn 定义服务注册函数类型
 | ||||
| 	// RegisterFn defines the method to register a server.
 | ||||
| 	RegisterFn func(*grpc.Server) | ||||
| 
 | ||||
| 	// Service 微服务实例
 | ||||
| 	Service struct { | ||||
| 		GrpcSrv *grpc.Server // gRPC服务器实例
 | ||||
| 		Opts    *Options     // 服务配置选项
 | ||||
| 		GrpcSrv *grpc.Server | ||||
| 		Opts    *Options | ||||
| 	} | ||||
| 
 | ||||
| 	// Options 服务配置选项
 | ||||
| 	Options struct { | ||||
| 		Addr        string                 // 服务监听地址
 | ||||
| 		EtcdClient  *clientv3.Client       // Etcd客户端
 | ||||
| 		MsConf      *conf.MicroServiceConf // 微服务配置
 | ||||
| 		GatewayConf *conf.GatewayConf      // 网关配置
 | ||||
| 		GatewayCtx  context.Context        // 网关上下文
 | ||||
| 		GatewayMux  *gwRuntime.ServeMux    // 网关多路复用器
 | ||||
| 		Addr        string | ||||
| 		EtcdClient  *clientv3.Client | ||||
| 		MsConf      *conf.MicroServiceConf | ||||
| 		GatewayConf *conf.GatewayConf | ||||
| 		GatewayCtx  context.Context | ||||
| 		GatewayMux  *gwRuntime.ServeMux | ||||
| 	} | ||||
| ) | ||||
| 
 | ||||
| // New 创建新的服务实例
 | ||||
| // srv: gRPC服务器实例
 | ||||
| // opts: 服务配置选项
 | ||||
| func New(srv *grpc.Server, opts *Options) *Service { | ||||
| 	return &Service{GrpcSrv: srv, Opts: opts} | ||||
| } | ||||
| 
 | ||||
| // Addr 将IP和端口格式化为host:port格式
 | ||||
| // ip: IP地址
 | ||||
| // port: 端口号
 | ||||
| func Addr(ip string, port int) string { | ||||
| 	return net.JoinHostPort(ip, strconv.Itoa(port)) | ||||
| } | ||||
| 
 | ||||
| // Start 启动服务
 | ||||
| // 包括etcd注册、gRPC服务启动、HTTP网关启动
 | ||||
| func (s *Service) Start() { | ||||
| 	printer.Info("[BSM - %s] Service Starting ...", vars.ServiceKey) | ||||
| 	print.Info("[BSM - %s] Service Starting ...", vars.ServiceKey) | ||||
| 
 | ||||
| 	// 注册到etcd
 | ||||
| 	// register to etcd.
 | ||||
| 	if s.Opts.MsConf != nil && s.Opts.MsConf.Enable { | ||||
| 		if s.Opts.EtcdClient == nil { | ||||
| 			printer.Error("[BSM Register] Etcd Client is nil.") | ||||
| 			print.Error("[BSM Register] Etcd Client is nil.") | ||||
| 			os.Exit(1) | ||||
| 		} | ||||
| 		printer.Info("[BSM - %s] Registering Service to Etcd ...", vars.ServiceKey) | ||||
| 		// 获取gRPC方法用于网关/路由发现
 | ||||
| 		print.Info("[BSM - %s] Registering Service to Etcd ...", vars.ServiceKey) | ||||
| 		// get methods
 | ||||
| 		methods := FoundGrpcMethods(s.GrpcSrv) | ||||
| 
 | ||||
| 		// 设置路由键
 | ||||
| 		// set router key
 | ||||
| 		routerKey := vars.ServiceRootPrefix + "Router/" + env.Runtime.Workspace + "/" + strings.ToLower(vars.ServiceKey) + "/" + s.Opts.Addr | ||||
| 
 | ||||
| 		// 使用租约注册到etcd
 | ||||
| 		// register to etcd
 | ||||
| 		register, err := RegisterService(s.Opts.EtcdClient, routerKey, methods, vars.ServiceLease) | ||||
| 		if err != nil { | ||||
| 			log.Panicf("[ERROR] %s Service Register:%s \n", vars.ServiceKey, err.Error()) | ||||
|  | @ -85,11 +73,11 @@ func (s *Service) Start() { | |||
| 
 | ||||
| 		register.SetAnonymous(anonKey, s.Opts.MsConf.Anonymous) | ||||
| 
 | ||||
| 		// 服务注册租约监听
 | ||||
| 		// service register lease
 | ||||
| 		go register.ListenLeaseRespChan() | ||||
| 	} | ||||
| 
 | ||||
| 	// 启动gRPC服务
 | ||||
| 	// run grpc srv.
 | ||||
| 	tcpListen, err := net.Listen("tcp", s.Opts.Addr) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
|  | @ -100,52 +88,31 @@ func (s *Service) Start() { | |||
| 			panic(err) | ||||
| 		} | ||||
| 	}() | ||||
| 	printer.Success("[BSM - %s] Grpc %s Runing Success !", vars.ServiceKey, s.Opts.Addr) | ||||
| 	print.Success("[BSM - %s] Grpc %s Runing Success !", vars.ServiceKey, s.Opts.Addr) | ||||
| 
 | ||||
| 	// 启动HTTP网关
 | ||||
| 	if s.Opts.GatewayConf != nil && s.Opts.GatewayConf.Enable { | ||||
| 		addr := Addr("0.0.0.0", s.Opts.GatewayConf.Port) | ||||
| 		go s.Gateway(s.Opts.Addr, addr) | ||||
| 
 | ||||
| 		printer.Success("[BSM - %s] Http %s Runing Success!", vars.ServiceKey, addr) | ||||
| 		print.Success("[BSM - %s] Http %s Runing Success!", vars.ServiceKey, addr) | ||||
| 	} | ||||
| 
 | ||||
| 	// 阻塞主线程
 | ||||
| 	select {} | ||||
| } | ||||
| 
 | ||||
| // Gateway 启动HTTP网关服务
 | ||||
| // grpcAddr: gRPC服务地址
 | ||||
| // httpAddr: HTTP服务地址
 | ||||
| func (s *Service) Gateway(grpcAddr string, httpAddr string) { | ||||
| 	// 定义上下文
 | ||||
| 	// 1. 定义一个context
 | ||||
| 	_, cancel := context.WithCancel(s.Opts.GatewayCtx) | ||||
| 	defer cancel() | ||||
| 
 | ||||
| 	// 启动HTTP服务,不因HTTP启动失败而导致panic
 | ||||
| 	if err := http.ListenAndServe(httpAddr, s.Opts.GatewayMux); err != nil { | ||||
| 		printer.Error("[BSM - %s] Http Serve Error: %v", vars.ServiceKey, err) | ||||
| 	} | ||||
| 	http.ListenAndServe(httpAddr, s.Opts.GatewayMux) | ||||
| } | ||||
| 
 | ||||
| // Use 执行初始化函数
 | ||||
| // initFunc: 初始化函数
 | ||||
| func (s *Service) Use(initFunc func() error) { | ||||
| 	err := (initFunc)() | ||||
| 	if err != nil { | ||||
| 		printer.Error(err.Error()) | ||||
| 		panic(err) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Stop 优雅停止服务
 | ||||
| func (s *Service) Stop() { | ||||
| 	s.GrpcSrv.GracefulStop() | ||||
| } | ||||
| 
 | ||||
| // FoundGrpcMethods 发现gRPC服务中的所有方法
 | ||||
| // s: gRPC服务器实例
 | ||||
| // 返回: 方法名列表
 | ||||
| // found grpc methods.
 | ||||
| func FoundGrpcMethods(s *grpc.Server) []string { | ||||
| 	var mothods []string | ||||
| 	for key, srv := range s.GetServiceInfo() { | ||||
|  |  | |||
|  | @ -1,36 +1,28 @@ | |||
| // Package types 提供通用数据类型定义
 | ||||
| // 包括分页、ID、身份标识等常用结构体
 | ||||
| package types | ||||
| 
 | ||||
| // Empty 空结构体
 | ||||
| type Empty struct { | ||||
| } | ||||
| 
 | ||||
| // Paginate 分页参数
 | ||||
| type Paginate struct { | ||||
| 	Offset int `form:"offset,default=0,range=[0:)"`  // 偏移量
 | ||||
| 	Size   int `form:"size,default=10,range=[1:50]"` // 每页大小
 | ||||
| 	Offset int `form:"offset,default=0,range=[0:)"` | ||||
| 	Size   int `form:"size,default=10,range=[1:50]"` | ||||
| } | ||||
| 
 | ||||
| // ID ID结构体
 | ||||
| type ID struct { | ||||
| 	ID uint `json:"id"` // ID字段
 | ||||
| 	ID uint `json:"id"` | ||||
| } | ||||
| 
 | ||||
| // Identity 身份标识结构体
 | ||||
| type Identity struct { | ||||
| 	Identity string `json:"identity"` // 身份标识
 | ||||
| 	Identity string `json:"identity"` | ||||
| } | ||||
| 
 | ||||
| // Ident ID和身份标识组合结构体
 | ||||
| type Ident struct { | ||||
| 	ID       uint   `json:"id"`       // ID字段
 | ||||
| 	Identity string `json:"identity"` // 身份标识
 | ||||
| 	ID       uint   `json:"id"` | ||||
| 	Identity string `json:"identity"` | ||||
| } | ||||
| 
 | ||||
| // FetchRequest 获取请求结构体
 | ||||
| type FetchRequest struct { | ||||
| 	Params    map[string]string `json:"params,optional"`     // 查询参数
 | ||||
| 	TimeRange string            `json:"time_range,optional"` // 时间范围
 | ||||
| 	Paginate                    // 分页参数
 | ||||
| 	Params    map[string]string `json:"params,optional"` | ||||
| 	TimeRange string            `json:"time_range,optional"` | ||||
| 	Paginate | ||||
| } | ||||
|  |  | |||
|  | @ -1,5 +1,3 @@ | |||
| // Package utils 提供通用工具函数
 | ||||
| // 包括数据类型转换、时间处理、网络工具等
 | ||||
| package utils | ||||
| 
 | ||||
| import ( | ||||
|  | @ -8,72 +6,61 @@ import ( | |||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| // String2Int 字符串转Int
 | ||||
| // intStr: 数字的字符串
 | ||||
| // 返回: 转换后的整数,转换失败返回0
 | ||||
| // 字符串转Int
 | ||||
| //
 | ||||
| //	intStr:数字的字符串
 | ||||
| func String2Int(intStr string) (intNum int) { | ||||
| 	intNum, err := strconv.Atoi(intStr) | ||||
| 	if err != nil { | ||||
| 		return 0 | ||||
| 	} | ||||
| 	intNum, _ = strconv.Atoi(intStr) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // String2Int64 字符串转Int64
 | ||||
| // intStr: 数字的字符串
 | ||||
| // 返回: 转换后的64位整数,转换失败返回0
 | ||||
| // 字符串转Int64
 | ||||
| //
 | ||||
| //	intStr:数字的字符串
 | ||||
| func String2Int64(intStr string) (int64Num int64) { | ||||
| 	intNum, err := strconv.ParseInt(intStr, 10, 64) | ||||
| 	if err != nil { | ||||
| 		return 0 | ||||
| 	} | ||||
| 	return intNum | ||||
| } | ||||
| 
 | ||||
| // String2Float64 字符串转Float64
 | ||||
| // floatStr: 小数点数字的字符串
 | ||||
| // 返回: 转换后的64位浮点数,转换失败返回0
 | ||||
| func String2Float64(floatStr string) (floatNum float64) { | ||||
| 	floatNum, err := strconv.ParseFloat(floatStr, 64) | ||||
| 	if err != nil { | ||||
| 		return 0 | ||||
| 	} | ||||
| 	intNum, _ := strconv.Atoi(intStr) | ||||
| 	int64Num = int64(intNum) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // String2Float32 字符串转Float32
 | ||||
| // floatStr: 小数点数字的字符串
 | ||||
| // 返回: 转换后的32位浮点数,转换失败返回0
 | ||||
| // 字符串转Float64
 | ||||
| //
 | ||||
| //	floatStr:小数点数字的字符串
 | ||||
| func String2Float64(floatStr string) (floatNum float64) { | ||||
| 	floatNum, _ = strconv.ParseFloat(floatStr, 64) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // 字符串转Float32
 | ||||
| //
 | ||||
| //	floatStr:小数点数字的字符串
 | ||||
| func String2Float32(floatStr string) (floatNum float32) { | ||||
| 	floatNum64, err := strconv.ParseFloat(floatStr, 32) | ||||
| 	if err != nil { | ||||
| 		return 0 | ||||
| 	} | ||||
| 	floatNum64, _ := strconv.ParseFloat(floatStr, 32) | ||||
| 	floatNum = float32(floatNum64) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // Int2String Int转字符串
 | ||||
| // intNum: 整数
 | ||||
| // 返回: 转换后的字符串
 | ||||
| // Int转字符串
 | ||||
| //
 | ||||
| //	intNum:数字字符串
 | ||||
| func Int2String(intNum int) (intStr string) { | ||||
| 	intStr = strconv.Itoa(intNum) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // Int642String Int64转字符串
 | ||||
| // intNum: 64位整数
 | ||||
| // 返回: 转换后的字符串
 | ||||
| // Int64转字符串
 | ||||
| //
 | ||||
| //	intNum:数字字符串
 | ||||
| func Int642String(intNum int64) (int64Str string) { | ||||
| 	// 10代表10进制
 | ||||
| 	//10, 代表10进制
 | ||||
| 	int64Str = strconv.FormatInt(intNum, 10) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // Float64ToString Float64转字符串
 | ||||
| // floatNum: float64数字
 | ||||
| // prec: 精度位数(不传则默认float数字精度)
 | ||||
| // 返回: 转换后的字符串
 | ||||
| // Float64转字符串
 | ||||
| //
 | ||||
| //	floatNum:float64数字
 | ||||
| //	prec:精度位数(不传则默认float数字精度)
 | ||||
| func Float64ToString(floatNum float64, prec ...int) (floatStr string) { | ||||
| 	if len(prec) > 0 { | ||||
| 		floatStr = strconv.FormatFloat(floatNum, 'f', prec[0], 64) | ||||
|  | @ -83,10 +70,10 @@ func Float64ToString(floatNum float64, prec ...int) (floatStr string) { | |||
| 	return | ||||
| } | ||||
| 
 | ||||
| // Float32ToString Float32转字符串
 | ||||
| // floatNum: float32数字
 | ||||
| // prec: 精度位数(不传则默认float数字精度)
 | ||||
| // 返回: 转换后的字符串
 | ||||
| // Float32转字符串
 | ||||
| //
 | ||||
| //	floatNum:float32数字
 | ||||
| //	prec:精度位数(不传则默认float数字精度)
 | ||||
| func Float32ToString(floatNum float32, prec ...int) (floatStr string) { | ||||
| 	if len(prec) > 0 { | ||||
| 		floatStr = strconv.FormatFloat(float64(floatNum), 'f', prec[0], 32) | ||||
|  | @ -96,9 +83,7 @@ func Float32ToString(floatNum float32, prec ...int) (floatStr string) { | |||
| 	return | ||||
| } | ||||
| 
 | ||||
| // BinaryToDecimal 二进制转10进制
 | ||||
| // bit: 二进制字符串
 | ||||
| // 返回: 转换后的十进制数
 | ||||
| // 二进制转10进制
 | ||||
| func BinaryToDecimal(bit string) (num int) { | ||||
| 	fields := strings.Split(bit, "") | ||||
| 	lens := len(fields) | ||||
|  | @ -111,9 +96,7 @@ func BinaryToDecimal(bit string) (num int) { | |||
| 	return | ||||
| } | ||||
| 
 | ||||
| // AnyToString 任意类型转字符串
 | ||||
| // in: 输入值
 | ||||
| // 返回: 转换后的字符串
 | ||||
| // interface to string
 | ||||
| func AnyToString(in any) (s string) { | ||||
| 	if in == nil { | ||||
| 		return "" | ||||
|  |  | |||
							
								
								
									
										10
									
								
								utils/dir.go
								
								
								
								
							
							
						
						
									
										10
									
								
								utils/dir.go
								
								
								
								
							|  | @ -25,17 +25,11 @@ func CreateDir(dirName string) bool { | |||
| } | ||||
| 
 | ||||
| func GetRunPath() string { | ||||
| 	path, err := os.Executable() | ||||
| 	if err != nil { | ||||
| 		return "" | ||||
| 	} | ||||
| 	path, _ := os.Executable() | ||||
| 	return path | ||||
| } | ||||
| 
 | ||||
| func GetCurrentPath() string { | ||||
| 	path, err := os.Getwd() | ||||
| 	if err != nil { | ||||
| 		return "" | ||||
| 	} | ||||
| 	path, _ := os.Getwd() | ||||
| 	return path | ||||
| } | ||||
							
								
								
									
										12
									
								
								utils/ext.go
								
								
								
								
							
							
						
						
									
										12
									
								
								utils/ext.go
								
								
								
								
							|  | @ -1,6 +1,10 @@ | |||
| package utils | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| ) | ||||
|  | @ -33,3 +37,11 @@ func ParseParams(in map[string]string) map[string]interface{} { | |||
| 	} | ||||
| 	return out | ||||
| } | ||||
| 
 | ||||
| func PrintJson(v any) { | ||||
| 	jsonBy, _ := json.Marshal(v) | ||||
| 	var out bytes.Buffer | ||||
| 	json.Indent(&out, jsonBy, "", "\t") | ||||
| 	out.WriteTo(os.Stdout) | ||||
| 	fmt.Printf("\n") | ||||
| } | ||||
|  |  | |||
|  | @ -8,14 +8,6 @@ import ( | |||
| 	"path/filepath" | ||||
| ) | ||||
| 
 | ||||
| 
 | ||||
| const ( | ||||
| 	B  = 1 | ||||
| 	KB = 1024 * B | ||||
| 	MB = 1024 * KB | ||||
| 	GB = 1024 * MB | ||||
| ) | ||||
| 
 | ||||
| // 将字符串写入文件
 | ||||
| func StringToFile(path, content string) error { | ||||
| 	startF, err := os.Create(path) | ||||
|  | @ -56,27 +48,6 @@ func CopyFile(src, dst string) (int64, error) { | |||
| 	return nBytes, err | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| func FileSize(fp string) string { | ||||
| 	fileInfo, err := os.Stat(fp) | ||||
| 	if err != nil { | ||||
| 		return "0 B" | ||||
| 	} | ||||
| 	bytes := fileInfo.Size() | ||||
| 
 | ||||
| 	switch { | ||||
| 	case bytes >= GB: | ||||
| 		return fmt.Sprintf("%.2f GB", float64(bytes)/float64(GB)) | ||||
| 	case bytes >= MB: | ||||
| 		return fmt.Sprintf("%.2f MB", float64(bytes)/float64(MB)) | ||||
| 	case bytes >= KB: | ||||
| 		return fmt.Sprintf("%.2f KB", float64(bytes)/float64(KB)) | ||||
| 	default: | ||||
| 		return fmt.Sprintf("%d B", bytes) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // 递归遍历文件夹
 | ||||
| // rootDir: 文件夹根目录
 | ||||
| // s: 存储文件名的切片
 | ||||
|  |  | |||
							
								
								
									
										34
									
								
								utils/fmt.go
								
								
								
								
							
							
						
						
									
										34
									
								
								utils/fmt.go
								
								
								
								
							|  | @ -1,34 +0,0 @@ | |||
| package utils | ||||
| 
 | ||||
| import ( | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| func FloatRoundString(in float64, place int) string { | ||||
| 
 | ||||
| 	return Float64ToString(FloatRound(in, place)) | ||||
| } | ||||
| 
 | ||||
| func FloatRound(in float64, place int) float64 { | ||||
| 	// 限制 place 范围在合理区间
 | ||||
| 	if place < 0 { | ||||
| 		place = 0 | ||||
| 	} | ||||
| 
 | ||||
| 	// 使用 strconv.FormatFloat 直接格式化,避免多次条件判断
 | ||||
| 	str := strconv.FormatFloat(in, 'f', place, 64) | ||||
| 	num, _ := strconv.ParseFloat(str, 64) | ||||
| 	return num | ||||
| } | ||||
| 
 | ||||
| func RateToFloat64(s string) float64 { | ||||
| 	// 去掉百分号
 | ||||
| 	s = strings.TrimSuffix(s, "%") | ||||
| 	// 转换为 float64
 | ||||
| 	f, err := strconv.ParseFloat(s, 64) | ||||
| 	if err != nil { | ||||
| 		return 0 | ||||
| 	} | ||||
| 	return f / 100 | ||||
| } | ||||
							
								
								
									
										22
									
								
								utils/net.go
								
								
								
								
							
							
						
						
									
										22
									
								
								utils/net.go
								
								
								
								
							|  | @ -1,5 +1,3 @@ | |||
| // Package utils 提供通用工具函数
 | ||||
| // 包括数据类型转换、时间处理、网络工具等
 | ||||
| package utils | ||||
| 
 | ||||
| import ( | ||||
|  | @ -15,9 +13,6 @@ import ( | |||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| // IsPublicIP 判断是否为公网IP
 | ||||
| // ipString: IP地址字符串
 | ||||
| // 返回: 是否为公网IP
 | ||||
| func IsPublicIP(ipString string) bool { | ||||
| 	ip := net.ParseIP(ipString) | ||||
| 	if ip.IsLoopback() || ip.IsLinkLocalMulticast() || ip.IsLinkLocalUnicast() { | ||||
|  | @ -25,11 +20,11 @@ func IsPublicIP(ipString string) bool { | |||
| 	} | ||||
| 	if ip4 := ip.To4(); ip4 != nil { | ||||
| 		switch true { | ||||
| 		case ip4[0] == 10: // 10.0.0.0/8
 | ||||
| 		case ip4[0] == 10: | ||||
| 			return false | ||||
| 		case ip4[0] == 172 && ip4[1] >= 16 && ip4[1] <= 31: // 172.16.0.0/12
 | ||||
| 		case ip4[0] == 172 && ip4[1] >= 16 && ip4[1] <= 31: | ||||
| 			return false | ||||
| 		case ip4[0] == 192 && ip4[1] == 168: // 192.168.0.0/16
 | ||||
| 		case ip4[0] == 192 && ip4[1] == 168: | ||||
| 			return false | ||||
| 		default: | ||||
| 			return true | ||||
|  | @ -38,8 +33,7 @@ func IsPublicIP(ipString string) bool { | |||
| 	return false | ||||
| } | ||||
| 
 | ||||
| // GetLocationIP 获取本机IP地址
 | ||||
| // 返回: 本机IP地址
 | ||||
| // Get Location IP .
 | ||||
| func GetLocationIP() (localIp string) { | ||||
| 	localIp = "127.0.0.1" | ||||
| 	// Get all network interfaces
 | ||||
|  | @ -201,7 +195,7 @@ func DownloadFile(url, saveTo string, fb func(length, downLen int64)) { | |||
| 	resp, err := client.Get(url) | ||||
| 	if err != nil { | ||||
| 		fmt.Printf("download %s error:%s\n", url, err) | ||||
| 		return | ||||
| 		os.Exit(0) | ||||
| 	} | ||||
| 	//读取服务器返回的文件大小
 | ||||
| 	fsize, err = strconv.ParseInt(resp.Header.Get("Content-Length"), 10, 32) | ||||
|  | @ -213,12 +207,12 @@ func DownloadFile(url, saveTo string, fb func(length, downLen int64)) { | |||
| 	file.Chmod(0777) | ||||
| 	if err != nil { | ||||
| 		fmt.Printf("Create %s error:%s\n", saveTo, err) | ||||
| 		return | ||||
| 		os.Exit(0) | ||||
| 	} | ||||
| 	defer file.Close() | ||||
| 	if resp.Body == nil { | ||||
| 		fmt.Printf("resp %s error:%s\n", url, err) | ||||
| 		return | ||||
| 		os.Exit(0) | ||||
| 	} | ||||
| 	defer resp.Body.Close() | ||||
| 	//下面是 io.copyBuffer() 的简化版本
 | ||||
|  | @ -256,6 +250,6 @@ func DownloadFile(url, saveTo string, fb func(length, downLen int64)) { | |||
| 
 | ||||
| 	if err != nil { | ||||
| 		fmt.Printf("callback error:%s\n", err) | ||||
| 		return | ||||
| 		os.Exit(0) | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -1,23 +1,13 @@ | |||
| // Package utils 提供通用工具函数
 | ||||
| // 包括时间处理、数据类型转换、网络工具等
 | ||||
| package utils | ||||
| 
 | ||||
| import ( | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| // Time2String 将时间转换为字符串
 | ||||
| // layout: 时间格式
 | ||||
| // t: 时间对象
 | ||||
| // 返回: 格式化的时间字符串
 | ||||
| func Time2String(layout string, t time.Time) string { | ||||
| 	return t.Format(layout) | ||||
| } | ||||
| 
 | ||||
| // String2Time 将字符串转换为时间
 | ||||
| // layout: 时间格式
 | ||||
| // in: 时间字符串
 | ||||
| // 返回: 时间对象
 | ||||
| func String2Time(layout, in string) time.Time { | ||||
| 	t, _ := time.ParseInLocation(layout, in, time.Local) | ||||
| 	return t | ||||
|  |  | |||
|  | @ -1,129 +0,0 @@ | |||
| package utils | ||||
| 
 | ||||
| import ( | ||||
| 	"regexp" | ||||
| 	"strings" | ||||
| 	"unicode" | ||||
| ) | ||||
| 
 | ||||
| // IsEmail 验证是否是邮箱格式
 | ||||
| func IsEmail(email string) bool { | ||||
| 	if strings.TrimSpace(email) == "" { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	// 邮箱正则表达式
 | ||||
| 	pattern := `^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$` | ||||
| 	matched, err := regexp.MatchString(pattern, email) | ||||
| 	if err != nil { | ||||
| 		return false | ||||
| 	} | ||||
| 	return matched | ||||
| } | ||||
| 
 | ||||
| // IsMobile 验证是否是手机号(中国手机号格式)
 | ||||
| func IsMobile(mobile string) bool { | ||||
| 	if strings.TrimSpace(mobile) == "" { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	// 中国手机号正则表达式:1开头,第二位3-9,后面9位数字
 | ||||
| 	pattern := `^1[3-9]\d{9}$` | ||||
| 	matched, err := regexp.MatchString(pattern, mobile) | ||||
| 	if err != nil { | ||||
| 		return false | ||||
| 	} | ||||
| 	return matched | ||||
| } | ||||
| 
 | ||||
| // IsNumber 验证是否是纯数字
 | ||||
| func IsNumber(str string) bool { | ||||
| 	if strings.TrimSpace(str) == "" { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	for _, char := range str { | ||||
| 		if !unicode.IsDigit(char) { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| // IsEnglish 验证是否是英文字符(不限大小写)
 | ||||
| func IsEnglish(str string) bool { | ||||
| 	if strings.TrimSpace(str) == "" { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	for _, char := range str { | ||||
| 		if !unicode.IsLetter(char) { | ||||
| 			return false | ||||
| 		} | ||||
| 		if !isEnglishLetter(char) { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| // IsEnglishWithSpace 验证是否是英文字符(允许空格)
 | ||||
| func IsEnglishWithSpace(str string) bool { | ||||
| 	if strings.TrimSpace(str) == "" { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	for _, char := range str { | ||||
| 		if unicode.IsSpace(char) { | ||||
| 			continue | ||||
| 		} | ||||
| 		if !unicode.IsLetter(char) { | ||||
| 			return false | ||||
| 		} | ||||
| 		if !isEnglishLetter(char) { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| // IsAlphanumeric 验证是否是字母和数字组合
 | ||||
| func IsAlphanumeric(str string) bool { | ||||
| 	if strings.TrimSpace(str) == "" { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	for _, char := range str { | ||||
| 		if !unicode.IsLetter(char) && !unicode.IsDigit(char) { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| // IsStrongPassword 验证强密码(至少8位,包含大小写字母和数字)
 | ||||
| func IsStrongPassword(password string) bool { | ||||
| 	if len(password) < 8 { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	var hasUpper, hasLower, hasDigit bool | ||||
| 
 | ||||
| 	for _, char := range password { | ||||
| 		switch { | ||||
| 		case unicode.IsUpper(char): | ||||
| 			hasUpper = true | ||||
| 		case unicode.IsLower(char): | ||||
| 			hasLower = true | ||||
| 		case unicode.IsDigit(char): | ||||
| 			hasDigit = true | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return hasUpper && hasLower && hasDigit | ||||
| } | ||||
| 
 | ||||
| // 辅助函数:判断是否是英文字母
 | ||||
| func isEnglishLetter(char rune) bool { | ||||
| 	return (char >= 'a' && char <= 'z') || (char >= 'A' && char <= 'Z') | ||||
| } | ||||
|  | @ -8,10 +8,4 @@ var ( | |||
| 	MemLRUMaxNumber int           = 1024 | ||||
| 	MemShardings    int           = 64 | ||||
| 	RedisShardings  int           = 256 | ||||
| 
 | ||||
| 	// CacheKeyPrefix 缓存键前缀
 | ||||
| 	CacheKeyPrefix = "bsm:" | ||||
| 
 | ||||
| 	// DefaultTTL 默认缓存过期时间
 | ||||
| 	DefaultTTL = 30 * time.Minute // 30分钟
 | ||||
| ) | ||||
|  |  | |||
|  | @ -1,12 +0,0 @@ | |||
| package vars | ||||
| 
 | ||||
| // LogLevel 日志级别
 | ||||
| type LogLevel int | ||||
| 
 | ||||
| const ( | ||||
| 	DEBUG LogLevel = iota | ||||
| 	INFO | ||||
| 	WARN | ||||
| 	ERROR | ||||
| 	FATAL | ||||
| ) | ||||
|  | @ -1,27 +0,0 @@ | |||
| package with | ||||
| 
 | ||||
| import ( | ||||
| 	"git.apinb.com/bsm-sdk/core/conf" | ||||
| 	"git.apinb.com/bsm-sdk/core/database" | ||||
| 	"git.apinb.com/bsm-sdk/core/printer" | ||||
| 	"git.apinb.com/bsm-sdk/core/types" | ||||
| 	"git.apinb.com/bsm-sdk/core/vars" | ||||
| 	"gorm.io/gorm" | ||||
| ) | ||||
| 
 | ||||
| func Databases(cfg *conf.DBConf, opts *types.SqlOptions) *gorm.DB { | ||||
| 	if cfg == nil || len(cfg.Source) == 0 { | ||||
| 		panic("No Database Source Found !") | ||||
| 	} | ||||
| 
 | ||||
| 	// print inform.
 | ||||
| 	printer.Info("[BSM - %s] Databases: %v", vars.ServiceKey, cfg) | ||||
| 
 | ||||
| 	var err error | ||||
| 	db, err := database.NewDatabase(cfg.Driver, cfg.Source, opts) | ||||
| 	if err != nil { | ||||
| 		printer.Error("Database Init Failed !") | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	return db | ||||
| } | ||||
							
								
								
									
										54
									
								
								with/etcd.go
								
								
								
								
							
							
						
						
									
										54
									
								
								with/etcd.go
								
								
								
								
							|  | @ -1,54 +0,0 @@ | |||
| package with | ||||
| 
 | ||||
| import ( | ||||
| 	"time" | ||||
| 
 | ||||
| 	"git.apinb.com/bsm-sdk/core/conf" | ||||
| 	"git.apinb.com/bsm-sdk/core/errcode" | ||||
| 	"git.apinb.com/bsm-sdk/core/printer" | ||||
| 	"git.apinb.com/bsm-sdk/core/vars" | ||||
| 	"go.etcd.io/etcd/client/pkg/v3/transport" | ||||
| 	clientv3 "go.etcd.io/etcd/client/v3" | ||||
| ) | ||||
| 
 | ||||
| func Etcd(cfg *conf.EtcdConf) (cli *clientv3.Client) { | ||||
| 	if cfg == nil || len(cfg.Endpoints) == 0 { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	etcdCfg := clientv3.Config{ | ||||
| 		Endpoints:   cfg.Endpoints, | ||||
| 		DialTimeout: 5 * time.Second, | ||||
| 	} | ||||
| 
 | ||||
| 	if cfg.Passwd != nil { | ||||
| 		etcdCfg.Username = cfg.Passwd.Account | ||||
| 		etcdCfg.Password = cfg.Passwd.Password | ||||
| 
 | ||||
| 	} | ||||
| 
 | ||||
| 	if cfg.TLS != nil { | ||||
| 		tlsInfo := transport.TLSInfo{ | ||||
| 			TrustedCAFile: cfg.TLS.CaFile, | ||||
| 			CertFile:      cfg.TLS.CertFile, | ||||
| 			KeyFile:       cfg.TLS.KeyFile, | ||||
| 		} | ||||
| 		tlsConfig, err := tlsInfo.ClientConfig() | ||||
| 		if err != nil { | ||||
| 			printer.Error(errcode.ErrEtcd.Error()) | ||||
| 			panic(err) | ||||
| 		} | ||||
| 		etcdCfg.TLS = tlsConfig | ||||
| 	} | ||||
| 
 | ||||
| 	var err error | ||||
| 	cli, err = clientv3.New(etcdCfg) | ||||
| 	if err != nil { | ||||
| 		printer.Error(errcode.ErrEtcd.Error()) | ||||
| 		panic(err) | ||||
| 	} | ||||
| 
 | ||||
| 	// print inform.
 | ||||
| 	printer.Info("[BSM - %s] Service Center: %v", vars.ServiceKey, cfg.Endpoints) | ||||
| 	return | ||||
| } | ||||
|  | @ -1,36 +0,0 @@ | |||
| package with | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"git.apinb.com/bsm-sdk/core/printer" | ||||
| 	"git.apinb.com/bsm-sdk/core/vars" | ||||
| 	"github.com/allegro/bigcache/v3" | ||||
| ) | ||||
| 
 | ||||
| func Memory(opts *bigcache.Config) (cli *bigcache.BigCache) { | ||||
| 	if opts == nil { | ||||
| 		opts = &bigcache.Config{ | ||||
| 			Shards:             1024, | ||||
| 			LifeWindow:         10 * time.Minute, | ||||
| 			CleanWindow:        5 * time.Minute, | ||||
| 			MaxEntriesInWindow: 1000 * 10 * 60, | ||||
| 			MaxEntrySize:       500, | ||||
| 			Verbose:            true, | ||||
| 			HardMaxCacheSize:   8192, | ||||
| 			OnRemove:           nil, | ||||
| 			OnRemoveWithReason: nil, | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	var err error | ||||
| 	cli, err = bigcache.New(context.Background(), *opts) | ||||
| 	if err != nil { | ||||
| 		printer.Error("Memory Cache Fatal Error") | ||||
| 		panic(err) | ||||
| 	} | ||||
| 
 | ||||
| 	printer.Success("[BSM - %s] Memory Cache: Shards=%d, MaxEntrySize=%d", vars.ServiceKey, opts.Shards, opts.MaxEntrySize) | ||||
| 	return | ||||
| } | ||||
|  | @ -1,18 +0,0 @@ | |||
| package with | ||||
| 
 | ||||
| import ( | ||||
| 	"git.apinb.com/bsm-sdk/core/cache/redis" | ||||
| 	"git.apinb.com/bsm-sdk/core/printer" | ||||
| 	"git.apinb.com/bsm-sdk/core/vars" | ||||
| ) | ||||
| 
 | ||||
| func RedisCache(cfg string) (cli *redis.RedisClient) { | ||||
| 	if cfg != "" { | ||||
| 		cli = redis.New(cfg, vars.ServiceKey) | ||||
| 
 | ||||
| 		// print inform.
 | ||||
| 		printer.Info("[BSM - %s] Cache: %s, DBIndex: %d", vars.ServiceKey, cfg, cli.DB) | ||||
| 	} | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
		Loading…
	
		Reference in New Issue