znlgis 博客

GIS开发与技术分享 — GDAL · GeoServer · PostGIS · QGIS · OpenLayers · Cesium · FreeCAD · NPOI

第17章 - 二次开发:扩展新动作类型

robotgo-flow 内置的 19 种动作已能覆盖大多数场景,但总有特殊需求需要自定义。得益于清晰的分层与接口设计,为框架添加一种新动作是一件有章可循的事。本章以「添加一个新动作类型」为主线,完整走一遍二次开发流程。

本章面向有 Go 基础、希望修改/扩展框架源码的读者。若你只是使用者,可略读了解扩展点即可。

17.1 扩展新动作的五个步骤

官方文档明确给出了添加新动作类型的标准流程,共五步,分布在不同的包中:

  1. internal/config/workflow.goAction 结构体中添加新字段,并更新 ActionType()Label() 方法;
  2. internal/action/ 下新建文件,实现 Runner 接口(Execute 方法);
  3. internal/action/factory.goFromConfig() 中添加对应分支;
  4. internal/config/loader.goisEmpty()validateSingleAction() 中处理新类型;
  5. internal/recorder/recorder.go 中添加录制支持。

下面我们以一个假想的新动作 hover(鼠标悬停) 为例,逐步实现。

17.2 第一步:定义配置字段

打开 internal/config/workflow.go,在 Action 结构体中新增字段:

type Action struct {
    // ... 已有字段 ...
    Hover any `yaml:"hover,omitempty"`   // string(模板) 或 {x, y}
}

hover 与 click 类似,支持模板或坐标,故用 any 类型。

接着更新 ActionType() 方法,让它能识别新动作:

func (a Action) ActionType() string {
    switch {
    // ... 已有分支 ...
    case a.Hover != nil:
        return "hover"
    }
    return "unknown"
}

再更新 Label(),为进度/日志显示提供可读标签:

func (a Action) Label() string {
    switch {
    // ... 已有分支 ...
    case a.Hover != nil:
        return "悬停"
    }
    return "未知动作"
}

17.3 第二步:实现 Runner

internal/action/ 下新建 hover.go,实现动作逻辑。参考 click.gotargetedAction 复用「模板或坐标」的定位逻辑:

package action

type HoverAction struct {
    targetedAction   // 复用模板/坐标解析
}

func (a *HoverAction) Execute(eng Engine) error {
    x, y, err := a.resolveTarget(eng)
    if err != nil {
        return err
    }
    return eng.Hover(x, y)   // 调用引擎的悬停能力
}

这里假设我们在 Engine 接口与引擎实现中新增了 Hover(x, y int) error 方法(内部可用 robotgo.Moverobotgo.MoveSmooth 把鼠标移到目标而不点击)。因此扩展新动作有时也要扩展 Engine 接口

  1. action/action.goEngine 接口中加 Hover(x, y int) error
  2. engine/engine.go 中实现 Hover
  3. action/engine_mock.go 的 Mock 中也补上 Hover,以便测试。

17.4 第三步:注册到工厂

打开 internal/action/factory.go,在 FromConfig 中添加对新字段的分支。可复用已有的 parseTargetedAction 泛型解析器:

func FromConfig(cfg config.Action, defaultTimeout int) (Runner, error) {
    switch {
    // ... 已有分支 ...
    case cfg.Hover != nil:
        base, err := parseTargetedAction(cfg.Hover)
        if err != nil {
            return nil, err
        }
        return &HoverAction{targetedAction: base}, nil
    }
    return nil, fmt.Errorf("未知动作类型")
}

这样,当 YAML 中出现 hover 字段时,工厂就能创建出 HoverAction

17.5 第四步:加入验证

打开 internal/config/loader.go,让验证逻辑认识新动作:

  • isEmpty() 中,把 Hover 纳入「非空」判断,否则含 hover 的动作会被误判为空动作;
  • validateSingleAction() 中,把 Hover 计入「已设置字段计数」,以确保「一个动作项只有一个动作字段」的规则对 hover 也生效;
  • 若 hover 用模板,还应确保 templatePaths() 能收集到它的模板路径,从而参与「模板文件存在性」校验。
func (a Action) isEmpty() bool {
    return a.Click == nil && /* ... */ && a.Hover == nil
}

17.6 第五步:支持录制

最后,在 internal/recorder/recorder.go 的动作菜单中加入新动作,让用户能在 record 交互录制时选择它:

  • askAction() 的菜单里增加一个选项(如「16. Hover」);
  • 对该选项调用 askTemplateOrCoord(复用模板/坐标询问逻辑),把结果写入 Action.Hover

至此,hover 动作就从「配置定义 → 执行实现 → 工厂注册 → 验证 → 录制」全链路打通,可以在 YAML 中这样使用:

- hover: "templates/menu_trigger.png"
- hover: {x: 400, y: 200}

17.7 测试你的新动作

框架自带完善的测试。二次开发后,应补充并运行测试:

cd src\go

# 运行所有测试
go test ./...

# 运行相关包测试
go test ./internal/config/ -v
go test ./internal/action/ -v
go test ./internal/executor/ -v

# 带覆盖率
go test ./... -cover

参考已有的测试文件:

  • config/loader_test.goconfig/loader_ext_test.go:配置加载与验证测试;
  • action/factory_test.go:工厂创建测试;
  • action/execute_test.go:动作执行集成测试(用 engine_mock.go 的 Mock 引擎);
  • action/interact_test.go:交互动作测试。

用 Mock 引擎测试动作是关键技巧:由于动作只依赖 Engine 接口,你可以在测试中传入 engine_mock.go 的 Mock,断言动作调用了哪些引擎方法、传了什么参数,而无需真的操作鼠标。为 HoverAction 写一个测试,验证它正确调用了 eng.Hover(x, y) 即可。

17.8 扩展其它维度

除了新增动作,你还可以在其它维度扩展框架:

  • 新增引擎能力:在 Engine 接口与实现中增加方法(如剪贴板高级操作、特定应用适配),供动作调用;
  • 新增交互方式:扩展 notify 包,接入更丰富的对话框或通知渠道;
  • 增强图像匹配:在 matcher.go 中引入容差匹配、多尺度匹配(应对 DPI 差异)等;
  • 新增前端:只要遵循 JSON-Line 协议,就能开发 Web、Electron 等其它前端来驱动 serve

17.9 二次开发注意事项

  • 保持接口契约:改动 Engine 接口时,务必同步更新真实实现与 Mock,否则编译不过;
  • 五步要齐全:漏掉验证(第四步)会导致新动作被当成空动作;漏掉工厂(第三步)会导致 YAML 里写了却不执行;
  • 复用已有解析器parseTargetedActionparseWait 等能省很多事,优先复用;
  • 补测试:新动作至少补一个基于 Mock 的执行测试;
  • 注意编码:涉及文件路径时记得走 encoding 转换。

17.10 小结

本章我们完整走了一遍二次开发流程——以新增 hover 动作为例,演示了官方规定的五个步骤:在 config 定义字段并更新 ActionType/Label、在 action 实现 Runner(必要时扩展 Engine 接口与 Mock)、在 factory 注册分支、在 loader 加入验证、在 recorder 支持录制。我们还讲了如何用 Mock 引擎测试动作、其它扩展维度以及注意事项。清晰的分层与接口设计,让扩展 robotgo-flow 变得有章可循。下一章,我们通过一个综合实战项目把所学融会贯通。