Matcher 链式调用最佳实践¶
最后更新: 2026-08-04
📋 概述¶
Remilia 的 Matcher 支持完整的链式调用,所有配置方法都返回 *Matcher。
本文档提供链式调用的最佳实践指南。
🎯 核心原则¶
原则 1: Handle 应该最后调用¶
// ✅ 推荐:配置在前,Handle 在后
eng.OnCommand("/ping").
SetDescription("测试连接").
SetPriority(100).
Handle(handler) // ← 最后(终结点,无返回值)
// ❌ 无法编译:Handle 是链式调用的终结点(返回 void),
// 从编译期杜绝 .Handle(h1).SetDescription(...) 这类误用。
// 如需在 Handle 之后继续操作,请提前保存 *Matcher:
m := eng.OnCommand("/ping").SetDescription("描述")
m.Handle(handler)
m.SetTemp(true) // 仍可操作 m
原因: - ✅ 逻辑清晰:先配置,后设置行为 - ✅ 易于阅读:符合自然的思维顺序 - ✅ 易于维护:配置集中在一起
元数据即时生效(2026-07 起):
SetDescription/SetUsage/SetAliases/SetHidden等元数据 setter 无论在注册前后调用,都会即时刷新命令缓存 (GetAllCommands//help 立即可见)。此前存在"注册瞬间生成缓存、事后修改 不刷新"的缺陷,链式写法的描述会在 /help 中显示为空。推荐顺序保持不变—— 配置前置仍是最清晰的写法。
原则 2: 相关配置应该分组¶
// ✅ 推荐:相关配置分组
eng.OnCommand("/admin").
// 基本信息
SetDescription("管理命令").
SetCategory("管理").
SetUsage("/admin <action>").
// 行为配置
SetPriority(100).
SetBlock(true).
// 中间件
Use(middleware.RequireAdmin()).
Use(middleware.Logging()).
// 处理器(最后)
Handle(adminHandler)
// ⚠️ 可以工作,但不够清晰
eng.OnCommand("/admin").
SetDescription("管理命令").
SetPriority(100).
SetCategory("管理").
Use(middleware.RequireAdmin()).
SetUsage("/admin <action>").
SetBlock(true).
Use(middleware.Logging()).
Handle(adminHandler)
原则 3: 复杂配置使用分步方式¶
// ✅ 推荐:复杂配置分步进行
m := eng.OnCommand("/complex")
// 基本信息
m.SetDescription("复杂命令")
m.SetCategory("高级")
m.SetUsage("/complex [options]")
// 添加多个中间件
m.Use(auth.RequireAdmin())
m.Use(middleware.SimpleRateLimit(10))
m.Use(middleware.Logging())
// 动态配置
if config.IsProduction() {
m.SetPriority(100)
} else {
m.SetPriority(50)
}
// 最后设置 Handler
m.Handle(complexHandler)
📖 完整示例¶
示例 1: 简单命令¶
// ✅ 最佳实践:简单命令使用完整链式
func registerPingCommand(eng *engine.Engine) {
eng.OnCommand(eventctx.EventGroup, "/ping").
SetDescription("测试机器人连接").
SetCategory("系统").
SetUsage("/ping").
Handle(func(ctx *context.Context) error {
ctx.Reply(platform.TextMessage("Pong! 🏓"))
return nil
})
}
示例 2: 带参数的命令¶
// ✅ 最佳实践:使用 Definition 定义参数
func registerSearchCommand(eng *engine.Engine) {
def := &command.Definition{
Name: "search",
Aliases: []string{"find", "query"},
Description: "搜索内容",
Usage: "/search <keyword> [--engine google]",
Category: "工具",
Examples: []string{
"/search Go语言",
"/search Python --engine bing",
},
Arguments: []*command.Argument{
{
Name: "keyword",
Description: "搜索关键词",
Required: true,
Type: command.ArgTypeString,
},
},
Flags: []*command.Flag{
{
Name: "engine",
ShortName: "e",
Description: "搜索引擎",
Default: "google",
},
},
}
eng.RegisterCommandDef(dto.GroupAtMessageCreate, def).
SetPriority(50).
Use(middleware.SimpleRateLimit(10)).
Handle(func(ctx *context.Context) error {
parsed := ctx.GetParsedCommand()
keyword := parsed.GetString("keyword")
engine := parsed.GetString("engine")
// 执行搜索...
return nil
})
}
示例 3: 需要权限的命令¶
// ✅ 最佳实践:分步配置复杂命令
func registerAdminCommand(eng *engine.Engine) {
m := eng.OnCommand(dto.GroupAtMessageCreate, "/admin")
// 基本信息
m.SetDescription("管理命令")
m.SetCategory("管理")
m.SetUsage("/admin <action>")
m.SetPermissions("admin")
// 行为配置
m.SetPriority(100)
m.SetBlock(true)
// 中间件链
m.Use(middleware.RequireAdmin())
m.Use(middleware.Logging())
m.Use(middleware.Metrics())
// 处理器(最后)
m.Handle(func(ctx *context.Context) error {
// 管理逻辑...
ctx.Reply("Admin command executed")
return nil
})
}
示例 4: 动态配置¶
// ✅ 最佳实践:动态配置时保存引用
func registerDynamicCommand(eng *engine.Engine, cfg *Config) {
m := eng.OnCommand(dto.GroupAtMessageCreate, "/dynamic")
// 基本配置
m.SetDescription("动态命令")
m.SetCategory(cfg.Category)
// 根据配置动态设置
if cfg.IsHighPriority {
m.SetPriority(100)
} else {
m.SetPriority(50)
}
if cfg.RequireAuth {
m.Use(auth.RequireAdmin())
}
if cfg.EnableRateLimit {
m.Use(middleware.SimpleRateLimit(cfg.RateLimit))
}
// Handler 最后设置
m.Handle(cfg.Handler)
}
⚠️ 常见错误¶
错误 1: Handle 在中间¶
// ❌ 错误:Handle 不应该在配置中间
eng.OnCommand("/bad").
SetDescription("描述").
Handle(handler). // ← 不应该在这里
SetPriority(100). // ← 配置应该在 Handle 之前
Use(middleware.Logging()) // ← 中间件也应该在 Handle 之前
问题: - 代码逻辑不清晰 - 难以理解配置顺序 - 中间件在 Handler 之后添加,容易混淆
修复:
// ✅ 正确:所有配置在前,Handle 在后
eng.OnCommand("/good").
SetDescription("描述").
SetPriority(100).
Use(middleware.Logging()).
Handle(handler)
错误 2: 不保存引用¶
// ❌ 错误:复杂配置不保存引用
eng.OnCommand("/complex").
SetDescription("描述1").
SetCategory("分类1").
SetUsage("用法1").
SetPriority(100).
SetBlock(true).
Use(middleware.A()).
Use(middleware.B()).
Use(middleware.C()).
SetPermissions("perm1", "perm2").
Handle(complexHandler)
// 太长,难以阅读
问题: - 链式调用过长 - 难以阅读和维护 - 不便于动态配置
修复:
// ✅ 正确:保存引用,分步配置
m := eng.OnCommand("/complex")
// 分组配置
m.SetDescription("描述1")
m.SetCategory("分类1")
m.SetUsage("用法1")
m.SetPriority(100)
m.SetBlock(true)
m.Use(middleware.A())
m.Use(middleware.B())
m.Use(middleware.C())
m.SetPermissions("perm1", "perm2")
m.Handle(complexHandler)
错误 3: 多次调用 Handle¶
// ⚠️ 注意:多次调用 Handle 会覆盖前一个
m := eng.OnCommand("/test")
m.Handle(func(ctx *context.Context) error {
ctx.Reply("Handler 1")
return nil
})
// 这会覆盖上面的 Handler
m.Handle(func(ctx *context.Context) error {
ctx.Reply("Handler 2") // ← 只有这个会执行
return nil
})
说明: - 多次调用 Handle 会覆盖,不会累加 - 最后一次调用的 Handler 生效
📐 链式调用顺序建议¶
推荐顺序¶
matcher.
// 1. 基本信息
SetDescription("...").
SetCategory("...").
SetUsage("...").
SetAliases("...").
SetExamples("...").
// 2. 行为配置
SetPriority(...).
SetBlock(...).
SetPermissions("...").
// 3. 中间件(按执行顺序)
Use(middleware1).
Use(middleware2).
Use(middleware3).
// 4. 处理器(最后)
Handle(handler)
分组建议¶
m := eng.OnCommand("/cmd")
// Group 1: 元数据(用于 Help)
m.SetDescription("描述")
m.SetCategory("分类")
m.SetUsage("用法")
m.SetExamples("示例1", "示例2")
// Group 2: 行为配置
m.SetPriority(100)
m.SetBlock(false)
// Group 3: 权限和中间件
m.SetPermissions("admin")
m.Use(auth.RequireAdmin())
m.Use(middleware.Logging())
// Group 4: 处理器(最后)
m.Handle(handler)
🎨 代码风格建议¶
风格 1: 链式(简单命令)¶
// 适用于:配置简单的命令(≤5 个配置)
eng.OnCommand("/ping").
SetDescription("测试连接").
Handle(pingHandler)
风格 2: 分步(复杂命令)¶
// 适用于:配置复杂的命令(>5 个配置)
m := eng.OnCommand("/admin")
m.SetDescription("管理命令")
m.SetCategory("管理")
m.SetPriority(100)
m.Use(middleware.RequireAdmin())
m.Use(middleware.Logging())
m.Handle(adminHandler)
风格 3: 混合(中等复杂度)¶
// 适用于:部分链式 + 部分分步
m := eng.OnCommand("/search").
SetDescription("搜索命令").
SetCategory("工具")
// 动态配置
if needAuth {
m.Use(auth.RequireAdmin())
}
m.Handle(searchHandler)
🔍 代码审查检查清单¶
在代码审查时,检查以下几点:
- Handle 是否在链式调用的最后?
- 相关配置是否分组?
- 复杂配置是否使用分步方式?
- 是否有 Handle 之后继续配置的情况?
- 中间件是否在 Handle 之前添加?
- 配置顺序是否清晰易读?
📚 参考¶
设计分析¶
🎯 快速参考¶
// ✅ 推荐模式
matcher.
SetDescription("..."). // 1. 基本信息
SetCategory("...").
SetPriority(...). // 2. 行为配置
Use(middleware). // 3. 中间件
Handle(handler) // 4. 处理器(最后)
// ❌ 避免模式
matcher.
Handle(handler). // ← 不应该在这里
SetDescription("...") // ← 配置应该在前面