refactor: modernize codebase for Go 1.26 and fix adapter issues

- Upgrade Go version from 1.24 to 1.26.1 with updated dependencies
- Replace interface{} with any type throughout codebase
- Add message deduplication in OneBot11 adapter to prevent duplicate event processing
- URL-encode access token in WebSocket connections to handle special characters
- Remove duplicate bot startup hooks in DI providers (now handled by botManager.StartAll)
- Use slices.Clone and errgroup.Go patterns for cleaner concurrent code
- Apply omitzero JSON tags for optional fields (Go 1.24+ feature)
This commit is contained in:
lafay
2026-03-18 01:22:49 +08:00
parent f3a72264af
commit f3220a2381
32 changed files with 2262 additions and 296 deletions

View File

@@ -0,0 +1,253 @@
# Go 现代化升级计划
## 项目概述
- **项目名称**: cellbot
- **当前 Go 版本**: 1.24.0
- **目标 Go 版本**: 1.26.1
- **分析日期**: 2026-03-17
## 升级摘要
通过代码扫描,识别出以下可使用现代 Go 特性升级的代码点:
| 升级类型 | 影响文件数 | 修改点数 | 优先级 |
|---------|-----------|---------|-------|
| `interface{}``any` | 22 | 134 | 高 |
| `omitempty``omitzero` | 6 | 32+ | 高 |
| `wg.Add(1)` + goroutine → `wg.Go()` | 2 | 3 | 中 |
| Slice/Map 工具函数优化 | 多处 | 若干 | 中 |
---
## 详细升级计划
### 1. 高优先级:`interface{}` 替换为 `any`
**Go 版本要求**: 1.18+
项目中发现 **134 处** `interface{}` 使用,可全部替换为更简洁的 `any`
#### 涉及文件列表
##### 协议层 (`internal/protocol/`)
| 文件 | 修改点 | 说明 |
|-----|-------|-----|
| [`interface.go`](internal/protocol/interface.go) | 2 | 接口定义 |
| [`message.go`](internal/protocol/message.go) | 5 | 消息段数据 |
| [`action.go`](internal/protocol/action.go) | 4 | 动作参数 |
| [`event.go`](internal/protocol/event.go) | 4 | 事件数据 |
| [`bot.go`](internal/protocol/bot.go) | 1 | SendAction 返回值 |
##### OneBot11 适配器 (`internal/adapter/onebot11/`)
| 文件 | 修改点 | 说明 |
|-----|-------|-----|
| [`types.go`](internal/adapter/onebot11/types.go) | 8 | 类型定义 |
| [`adapter.go`](internal/adapter/onebot11/adapter.go) | 6 | 适配器方法 |
| [`action.go`](internal/adapter/onebot11/action.go) | 15 | 动作构建函数 |
| [`event.go`](internal/adapter/onebot11/event.go) | 14 | 事件解析 |
| [`message.go`](internal/adapter/onebot11/message.go) | 12 | 消息转换 |
| [`client.go`](internal/adapter/onebot11/client.go) | 2 | HTTP 客户端 |
##### Milky 适配器 (`internal/adapter/milky/`)
| 文件 | 修改点 | 说明 |
|-----|-------|-----|
| [`types.go`](internal/adapter/milky/types.go) | 4 | 类型定义 |
| [`adapter.go`](internal/adapter/milky/adapter.go) | 4 | 适配器方法 |
| [`bot.go`](internal/adapter/milky/bot.go) | 15 | Bot 方法 |
| [`event.go`](internal/adapter/milky/event.go) | 20+ | 事件转换 |
| [`api_client.go`](internal/adapter/milky/api_client.go) | 2 | API 客户端 |
##### 引擎模块 (`internal/engine/`)
| 文件 | 修改点 | 说明 |
|-----|-------|-----|
| [`middleware.go`](internal/engine/middleware.go) | 3 | 指标数据 |
| [`plugin.go`](internal/engine/plugin.go) | 4 | 插件处理 |
##### 插件模块 (`internal/plugins/`)
| 文件 | 修改点 | 说明 |
|-----|-------|-----|
| [`welcome/welcome.go`](internal/plugins/welcome/welcome.go) | 4 | 欢迎消息 |
| [`mcstatus/mcstatus.go`](internal/plugins/mcstatus/mcstatus.go) | 1 | 状态命令 |
| [`mcstatus/ping.go`](internal/plugins/mcstatus/ping.go) | 5 | ping 解析 |
#### 示例修改
```go
// 修改前
func (a *BaseAction) Execute(ctx interface{}) (map[string]interface{}, error)
// 修改后
func (a *BaseAction) Execute(ctx any) (map[string]any, error)
```
---
### 2. 高优先级:`omitempty` 替换为 `omitzero`
**Go 版本要求**: 1.24+
对于 `time.Duration``time.Time`、结构体、切片、map 类型,应使用 `omitzero` 替代 `omitempty`,因为 `omitempty` 对这些类型不生效。
#### 涉及文件列表
| 文件 | 建议修改字段 |
|-----|------------|
| [`internal/adapter/onebot11/types.go`](internal/adapter/onebot11/types.go) | `Duration int64`, `Interval int64` 等数值字段 |
| [`internal/adapter/onebot11/action.go`](internal/adapter/onebot11/action.go) | `Duration int64`, `Delay int` 等时长字段 |
| [`internal/adapter/milky/types.go`](internal/adapter/milky/types.go) | `ExpireTime *int64`, `ShutUpEndTime *int64` 等时间字段 |
| [`internal/protocol/event.go`](internal/protocol/event.go) | `GroupID string`, `UserID string` 等可选字段 |
#### 示例修改
```go
// 修改前
type SetGroupBanAction struct {
GroupID int64 `json:"group_id"`
Duration int64 `json:"duration,omitempty"` // 0 表示取消禁言
}
// 修改后
type SetGroupBanAction struct {
GroupID int64 `json:"group_id"`
Duration int64 `json:"duration,omitzero"` // 0 表示取消禁言
}
```
---
### 3. 中优先级:`wg.Go()` 替换 `wg.Add(1)` + goroutine
**Go 版本要求**: 1.25+
项目中发现 **3 处** 可使用更简洁的 `wg.Go()` 模式。
#### 涉及文件
| 文件 | 行号 | 当前代码 |
|-----|-----|---------|
| [`internal/engine/scheduler.go`](internal/engine/scheduler.go:332) | 332-333 | `j.wg.Add(1)` + `go j.run()` |
| [`internal/engine/scheduler.go`](internal/engine/scheduler.go:431) | 431-432 | `j.wg.Add(1)` + `go j.run()` |
| [`internal/engine/eventbus.go`](internal/engine/eventbus.go:66) | 66-67 | `eb.wg.Add(1)` + `go eb.dispatch()` |
#### 示例修改
```go
// 修改前 (scheduler.go:332-333)
j.wg.Add(1)
go j.run()
// 修改后
j.wg.Go(j.run)
```
**注意**: 需要调整 `run()` 方法的签名,移除 `defer j.wg.Done()` 调用。
---
### 4. 中优先级:使用 `slices` 和 `maps` 包
**Go 版本要求**: 1.21+
#### 可优化的代码模式
项目中存在一些可以优化的 slice 操作:
| 文件 | 当前模式 | 建议优化 |
|-----|---------|---------|
| [`internal/engine/eventbus.go:222-224`](internal/engine/eventbus.go:222) | 手动复制切片 | `slices.Clone()` |
| [`internal/engine/dispatcher.go:240-242`](internal/engine/dispatcher.go:240) | 手动复制切片 | `slices.Clone()` |
#### 示例修改
```go
// 修改前
subsCopy := make([]*Subscription, len(subs))
copy(subsCopy, subs)
// 修改后
subsCopy := slices.Clone(subs)
```
---
### 5. 低优先级:其他现代 Go 特性
#### 5.1 使用 `time.Since` 替换 `time.Now().Sub()`
项目已正确使用 `time.Since`,无需修改。
#### 5.2 使用 `min`/`max` 内置函数
项目中未发现明显的 if-else 比较可替换为 `min`/`max`
#### 5.3 使用 `cmp.Or` 替换空值检查
项目中未发现明显的空字符串检查可替换为 `cmp.Or`
---
## 执行计划
### 阶段 1升级 Go 版本
1. 更新 [`go.mod`](go.mod) 文件:
```go
go 1.26.1
toolchain go1.26.1
```
2. 运行 `go mod tidy` 更新依赖
### 阶段 2高优先级修改
1. 批量替换 `interface{}` → `any`134 处)
2. 更新 `omitempty` → `omitzero`32+ 处)
### 阶段 3中优先级修改
1. 重构 `scheduler.go` 和 `eventbus.go` 使用 `wg.Go()`
2. 使用 `slices.Clone()` 优化切片复制
### 阶段 4验证
1. 运行 `go build ./...` 确保编译通过
2. 运行 `go test ./...` 确保测试通过
3. 运行 `go vet ./...` 进行静态分析
---
## 风险评估
| 风险 | 等级 | 缓解措施 |
|-----|-----|---------|
| `any` 替换导致类型推断问题 | 低 | 编译器会捕获所有类型错误 |
| `omitzero` 行为差异 | 中 | 仔细测试 JSON 序列化/反序列化 |
| `wg.Go()` API 不兼容 | 低 | Go 1.25+ 已稳定支持 |
---
## 预期收益
1. **代码可读性提升**`any` 比 `interface{}` 更简洁
2. **JSON 序列化正确性**`omitzero` 正确处理零值
3. **并发代码简化**`wg.Go()` 减少样板代码
4. **性能优化**`slices.Clone()` 可能使用内部优化
---
## 总结
本升级计划将项目从 Go 1.24 升级到 Go 1.26.1,并应用现代 Go 语法特性。主要修改集中在:
1. **134 处** `interface{}` → `any` 替换
2. **32+ 处** `omitempty` → `omitzero` 更新
3. **3 处** `wg.Go()` 简化
4. **多处** `slices.Clone()` 优化
建议按阶段逐步实施,每个阶段完成后进行验证,确保代码质量和功能稳定性。