254 lines
7.5 KiB
Markdown
254 lines
7.5 KiB
Markdown
|
|
# 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()` 优化
|
|||
|
|
|
|||
|
|
建议按阶段逐步实施,每个阶段完成后进行验证,确保代码质量和功能稳定性。
|