该功能特性从v2.1版本开始提供。

基本介绍

设计背景

在业务项目实践中,业务逻辑封装往往是最复杂的部分,同时,业务模块之间的依赖十分复杂、边界模糊,无法采用Golang包管理的形式。如何有效管理项目中的业务逻辑封装部分,对于每个采用Golang开发的项目都是必定会遇到的难题。

在标准的软件设计流程中,模块与模块之间的依赖会先明确接口定义,在软件开发的实施过程中再通过代码来具体实现。但在大部分高节奏的互联网工程下,并没有严谨的软件设计流程,甚至开发人员的质量水平也参差不齐,大部分开发人员首先关心的是如何去实现需求场景对应的功能逻辑,尽可能地提高开发效率。

设计目标

提供一种代码管理方式,可以通过具体模块实现直接生成模块接口定义、模块注册代码。

简化业务逻辑实现与接口分离的实现,降低模块方法与接口定义的重复操作,提高模块与模块之间的透明度与调用便捷性。

设计实现

  1. 增加logic分类目录,将所有业务逻辑代码迁移到logic分类目录下,采用包管理形式来管理业务模块。
  2. 业务模块之间的依赖通过接口化解耦,将原有的service分类调整为接口目录。这样每个业务模块将会各自维护、更加灵活。
  3. 可以按照一定的编码规范,从logic业务逻辑代码生成service接口定义代码。同时,也允许人工维护这部分service接口。

注意事项

再次提醒,通过logic实现去生成service接口并不是一个代码管理的标准化做法,只是提供另一个可供选择的、便捷的代码管理方式。这种管理方式有优点也有缺点,优点是针对大部分场景的业务模块的接口自动生成比较方便;缺点是无法识别语法继承关系,无法生成父级嵌套类型的方法。

框架的工程管理当然也支持标准的接口代码管理方式,即支持先定义service接口,再编码logic具体实现。需要注意的是,这个service的源代码中不能出现顶部工具的注释信息(工具依靠这个注释来判断该文件是否可覆盖😈),很多同学复制粘贴的时候把文件顶部注释保留了,就会引起手动维护接口文件失效。具体见截图注释:

命令使用

该命令通过分析给定的logic业务逻辑模块目录下的代码,自动生成service目录接口代码。

需要注意:

  1. 由于该命令是根据业务模块生成service接口,因此只会解析二级目录下的go代码文件,并不会无限递归分析代码文件。以logic目录为例,该命令只会解析logic/xxx/*.go文件。因此,需要logic层代码结构满足一定规范。
  2. 不同业务模块中定义的结构体名称在生成的service接口名称时可能会重复覆盖,因此需要在设计业务模块时保证名称不能冲突。

该命令的示例项目请参考:https://github.com/gogf/gf-demo-user

手动模式

如果是手动执行命令行,直接在项目根目录下执行 gf gen service 即可。

$ gf gen service -h
USAGE
    gf gen service [OPTION]

OPTION
    -s, --srcFolder         source folder path to be parsed. default: internal/logic
    -d, --dstFolder         destination folder path storing automatically generated go files. default: internal/service
    -f, --dstFileNameCase   destination file name storing automatically generated go files, cases are as follows:
                            | Case            | Example            |
                            |---------------- |--------------------|
                            | Lower           | anykindofstring    |
                            | Camel           | AnyKindOfString    |
                            | CamelLower      | anyKindOfString    |
                            | Snake           | any_kind_of_string | default
                            | SnakeScreaming  | ANY_KIND_OF_STRING |
                            | SnakeFirstUpper | rgb_code_md5       |
                            | Kebab           | any-kind-of-string |
                            | KebabScreaming  | ANY-KIND-OF-STRING |
    -w, --watchFile         used in file watcher, it re-generates all service go files only if given file is under
                            srcFolder
    -a, --stPattern         regular expression matching struct name for generating service. default: ^s([A-Z]\\w+)$
    -p, --packages          produce go files only for given source packages
    -i, --importPrefix      custom import prefix to calculate import path for generated importing go file of logic
    -l, --clear             delete all generated go files that are not used any further
    -h, --help              more information about this command

EXAMPLE
    gf gen service
    gf gen service -f Snake

如果使用框架推荐的项目工程脚手架,并且系统安装了make工具,也可以使用make service快捷指令。

参数说明:

名称必须默认值含义
srcFolderinternal/logic指向logic代码目录地址
dstFolderinternal/service指向生成的接口文件存放目录
dstFileNameCaseSnake生成的文件名名称格式
stPatterns([A-A]\w+)

使用正则指定业务模块结构体定义格式,便于解析业务接口定义名称。在默认的正则下,所有小写s开头,大写字母随后的结构体都将被当做业务模块接口名称。例如:

logic结构体名称service接口名称
sUserUser
sMetaDataMetaData
watchFile

用在代码文件监听中,代表当前改变的代码文件路径

packages

仅生成指定包名的接口文件,给定字符串数组,通过命令行传参则给定JSON字符串,命令行组件自动转换数据类型
importPrefix

指定生成业务引用文件中的引用包名前缀
overwrite
true生成代码文件时是否覆盖已有文件
clear
false自动删除logic中不存在的接口文件(仅删除自动维护的文件)

自动模式

Goland/Idea

如果您是使用的GolandIDE,那么可以使用我们提供的配置文件:watchers.xml  自动监听代码文件修改时自动生成接口文件。使用方式,如下图:

Visual Studio Code

如果您是使用的Visual Studio Code,那么可以安装插件 RunOnSave 随后配置插件:

		"emeraldwalk.runonsave": {
			"commands": [
				{
					"match": ".*logic.*go",
					"isAsync": true,
					"cmd": "gf gen service"
				}
			]
		}

具体使用手摸手

Step1:引入我们提供的配置

我们建议您在使用Goland IDE时,使用我们提供的配置文件:watchers.xml 

Step2:编写您的业务逻辑代码

Step3:生成接口及服务注册文件

如果您已经按照Step1做好了配置,那么这一步可以忽略。因为在您编写代码的时候,service便同时生成了接口定义文件。

否则,每一次在您开发/更新完成logic业务模块后,您需要手动执行一下 gf gen service 命令。

Step4:注意服务的实现注入部分(仅一次)

只有在生成完成接口文件后,您才能在每个业务模块中加上接口的具体实现注入。该方法每个业务模块加一次即可。

Step5:在启动文件中引用接口实现注册(仅一次)

可以发现,该命令除了生成接口文件之外,还生成了一个接口实现注册文件。该文件用于在程序启动时,将接口的具体实现在启动时执行注册。

该文件的引入需要在main包的最顶部引入,需要注意import的顺序,放到最顶部,后面加一个空行。如果同时存在packed包的引入,那么放到packed包后面。像这样:

Step6:Start&Enjoy

启动main.go即可。

常见问题FAQ

logic中的结构体存在嵌套时,无法自动生成嵌套类型的方法

这种场景建议手动维护service接口定义,不使用工具的自动生成。手动维护的接口定义文件不会被工具覆盖,手动和自动可以同时使用。

快速定位接口的具体实现

项目业务模块采用接口化解耦后体验非常棒!但是我在开发和调试过程中,想要快速找到指定接口的具体实现有点困难,能给点指导思路吗?

> 这里我推荐使用Goland IDE,有个很棒的接口实现定位功能,具体如图。找到接口定义后,点击左边的小图标可快速定位具体的实现。如果Goland不显示小图标,可以尝试升级使用最新版本的Goland哈。

或者在左侧没有小图标的时候,可以右键选择 Go To → Implementation(s)





Content Menu

  • No labels

33 Comments

  1. // ==========================================================================
    // Code generated by GoFrame CLI tool. DO NOT EDIT.
    // ==========================================================================

    package service

    import (
    "context"
    "pms/internal/consts"
    "pms/internal/dao"
    "pms/internal/model"
    "pms/internal/model/do"
    "pms/internal/model/entity"
    "pms/utility"

    "github.com/gogf/gf/v2/frame/g"
    "github.com/gogf/gf/v2/os/grpool"
    )

    生成的service 文件的import 部分不正确是什么原因呢?
    1. GF Version:  v2.1.2 已经解决.

  2. 生成service的文件名全是小写,是不是应该与service保持一致.

    1. 针对这个问题有给强哥提了PR,希望能被合并~
      https://github.com/gogf/gf/pull/1953

      1. 我看了代码,好像是刻意转成小写的呢.另外请教您一下.我测试时生成的service会一些用不到的包,这个您了解是什么问题吗?

        1. 强哥说是bug,目前可以通过 goimports 工具自动清理多出包。

    2. GF Version:  v2.1.2 已经解决.

  3. 生成service时,没有判断逻辑代码是否注释.即使注释的方法也会生成;

  4.  这里我推荐使用GolandIDE,有个很棒的接口实现定位功能,具体如图。找到接口定义后,点击左边的小图标可快速定位具体的实现。

    Code generated by GoFrame CLI tool. DO NOT EDIT.

    老大,这个要把生成的头部修改一下,小图标才能出现.有上边这个就没有小图标.

  5. 自定义目录,会提示:

    source folder path "internal/logic" does not exist

  6. 郭强 强仔  郭老板 您的GolandIDE 什么版本的,我这没有你说的那个小图标呀?

  7. 强仔  把文件的

    Code generated by GoFrame CLI tool. DO NOT EDIT.  删除了就有小图标了!
  8. 请问我用gf gen service 为什么生成不了service文件,都需要哪些步骤 

    logic 文件已经写好了

    ll internal/logic/
    drwxr-xr-x  3 root root 4096 Sep 16 17:18 ./
    drwxr-xr-x 11 root root 4096 Sep 14 15:51 ../
    drwxr-xr-x  2 root root 4096 Sep 16 17:18 camera_type/


    gf gen service
    done!

    只是返回了done 然后就没了

    servcice还是空的


  9. 你的那个是手动执行,自动不香么。。。
    但我的也是,怀疑是作者偷懒。你需要自己先写一下注入和接口名称,将接口和实现函数关联(就是类似java中的构造方法,我总是搞混,魔怔了),然后你改动logic中实现方法,就会自动自动创建service中接口,不需要每次都改。

    1. 谢谢回复,不用自动是因为电脑用goland占用内存太大,然后我这这边排查了下,发现问题有个logic的返回值少了一个空格,这可太难受了

  10. gcf

    请问如何将logic进行分组生产不同分组service?

    当前gf gen logic仅可以对logic下面的儿子目录生产service,因为项目稍微大了之后,logic就需要进分组了,需要将某些逻辑分组生产不同的service进行保存。研究了半天没研究出来该怎么做。求破!

  11. 有没有针对 vscode 的插件哦

  12. 我在logic层的方法返回了一个errors.New("xxxx")这种的,会导致生成的service中多一行

    import errors

    但是这个引用在service文件中没有使用,打包的时候就会一直提示,当然可以手动删掉,但是每次生成又会从新出现

  13. 如果在logic上继承了某个结构体,例如gf-jwt

    type sAuth struct {
       *jwt.GfJWTMiddleware
    }

    生成出来的service并没有jwt.GfJWTMiddleware里面的方法,这样请问要如何解决?

  14. glq

    强哥,我已经无计可施。生成的问题件只有两行

    // ================================================================================
    // Code generated by GoFrame CLI tool. DO NOT EDIT.
    // You can delete these comments if you wish manually maintain this interface file.
    // ================================================================================

    package service

    type ()

    不管我怎么生成都只有这些,手动也好或者是自动也好

    我的logic内容是:
    package devices

    import (
    "context"
    "xxx_xxx_xxx_xxx/internal/model"
    )

    /**
    * @Date: 2023/3/2 20:54
    * @Desc:
    */

    type devices struct{}

    func init() {
    //service.RegisterDevices(New())

    }

    func New() *devices {
    return &devices{}
    }

    func (this *devices) GetList(ctx context.Context, in *model.DeviceListIn) (out *model.DeviceListOut, err error) {

    return
    }

    不管我怎么修改,包括重新新建logic文件等,都不能正确生成service文件,求助
    1. glq

      我去,终于找到问题所在,原来违反了命名规则,都怪我没有好好看文档,最后将

      devices => sDevices 解决了问题。
      1. 规则是可配置的,具体可以看下命令帮助或文档。

  15. logic 中注释的内容也会生成 service,在刚开始使用的时候会有一些困扰,建议增加说明或者开关

  16. pei

    生成service文件名,加上前缀怎么弄?比如 service/demo.go 到 service/c_demo.go 求助。

  17. 郭强 大佬,反馈一个BUG,gf gen service 命令,会把注释了的方法也生产到service接口中。

    1. 新版本已经修复了

  18. 大佬,请问一下执行gf gen service -s internal/app/system/logic -d internal/app/system/service命令报错:2:1: expected 'package', found '*' (and 4 more errors)是什么原因呀,从master拉下来的代码,可运行!方便解答一下吗


      1. 我也遇到了,请问怎么处理的。

  19. 郭强  大佬, 执行 gf gen service  的时候, 可以保证生成的顺序是一样的吗? 
    现在的问题是: 所有文件都没有改动, 重复执行gf gen service的时候, 文件一直在变动. 

  20. gf 2.4.1不能直接升级到2.4.4吗?
    start upgrading version...
    start upgrading cli...
    start downloading "https://github.com/gogf/gf/releases/latest/download/gf_windows_amd64" to "E:\goproject\bin\gf.exe~", it may take some time
    download "https://github.com/gogf/gf/releases/latest/download/gf_windows_amd64" to "E:\goproject\bin\gf.exe~" failed: Head "https://github.com/gogf/gf/releases/latest/download/gf_windows_amd64": dial tcp 20.205.243.166:443: connectex: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.

    1. 之前的版本在Windows下有bug,需要手动下载安装最新版。https://github.com/gogf/gf/releases/latest/download/gf_windows_amd64.exe

  21. 请教一下,就是我在生成service接口时logic.go不是文件而是生成了一个文件夹怎么办???