Files
cellbot/plans/go-modernization-plan.md
lafay f3220a2381 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)
2026-03-18 01:22:49 +08:00

254 lines
7.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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()` 优化
建议按阶段逐步实施,每个阶段完成后进行验证,确保代码质量和功能稳定性。