【Golang】使用 goctl 进行代码生成——Go-Zero 实践初探

go-zero学习 — 基础 阅读详情

前言

在学习go-zero的过程中,我简单记录了http、rpc服务的代码生成方法,以及mysql和mongodb的数据库模型代码生成步骤,作为备忘录使用。初学,如有不妥之处,感谢指正。

包结构

这部分借用官网内容,介绍了生成代码的包结构。

工程维度

.
├── consumer
├── go.mod
├── internal
│   └── model
├── job
├── pkg
├── restful
├── script
└── service
  • consumer: 队列消费服务
  • internal: 工程内部可访问的公共模块
  • job: cron job 服务
  • pkg: 工程外部可访问的公共模块
  • restful:HTTP 服务目录,下存放以服务为维度的微服务
  • script:脚本服务目录,下存放以脚本为维度的服务
  • service:gRPC 服务目录,下存放以服务为维度的微服务

服务维度

example
├── etc
│   └── example.yaml
├── main.go
└── internal    
	├── config    
	│   └── config.go 
	├── handler    
	│   ├── xxxhandler.go
    │   └── xxxhandler.go    
    ├── logic    
    │   └── xxxlogic.go    
    ├── svc    
    │   └── servicecontext.go    
    └── types    
        └── types.go
  • example:单个服务目录,一般是某微服务名称
  • etc:静态配置文件目录
  • main.go:程序启动入口文件
  • internal:单个服务内部文件,其可见范围仅限当前服务
  • config:静态配置文件对应的结构体声明目录
  • handler:handler 目录,可选,一般 http 服务会有这一层做路由管理,handler 为固定后缀
  • logic:业务目录,所有业务编码文件都存放在这个目录下面,logic 为固定后缀
  • svc:依赖注入目录,所有 logic 层需要用到的依赖都要在这里进行显式注入
  • types:结构体存放目录

Http服务代码生成

api描述http服务,编写好api文件后,使用命令goctl api go --api xx.api --dir . 在指定目录生成文件。
注意修改api文件重新生成后记得检查一下是否代码逻辑成功修改了,有些修改不会被检测到,此时要删掉文件重新生成。
api指令常见的用法如下表所示,更详细的用法可以参考 https://go-zero.dev/docs/tutorials/cli/api 。前端代码生成似乎略微有些地方不兼容。

功能命令说明
生成 Go 后端服务goctl api go --api <api_file> --dir <output_dir> --style <style>根据 .api 文件生成完整的 HTTP 后端服务代码。 --api:输入 .api 文件路径;
--dir:代码输出目录;--style:文件命名风格(gozero/goZero/go_zero)。
创建 .api 模板文件goctl api new --style <style> <service_name>创建包含示例语法的 .api 模板文件。 <service_name>:生成 service_name.api 文件; --style:文件命名风格。
格式化 .api 文件goctl api format --dir <api_file_or_dir>自动格式化指定的 .api 文件或目录。
校验 .api 文件goctl api validate --api <api_file>语法检查(不生成代码),适合在提交前或 CI 流程中使用。
生成 TS/JS 前端代码goctl api ts --api <api_file> --dir <output_dir>生成前端调用代码(请求函数 + 类型定义)。 --api:api文件;--dir:输出文件夹;下面所有客户端代码同理。
生成 Dart 客户端代码goctl api dart --api <api_file> --dir <output_dir> 生成 Dart(用于 Flutter)的客户端调用代码(请求函数 + 类型定义)。
生成 Kotlin 客户端代码goctl api kt --api <api_file> --dir <output_dir> --pkg xx生成 Kotlin 语言的客户端调用代码。
生成 文档 文件goctl api doc --dir <api_dir> --o <output_dir>生成Markdown说明文件。--dir:api文件所在目录;--o 文档输出目录。
生成 Swagger 文档goctl api swagger --api <api_file> --dir <output_dir> --filename <filename>生成 swagger.json 文件。 --filename:指定输出文件名(可选,默认 swagger.json)。
自定义文件生成goctl api plugin --plugin <plugin_name> --api <api_file> --dir <output_dir>使用插件生成自定义文件。 --plugin:插件名称

一个包含所有语法块的完整写法如下:
这是主api文件,hello.api。

syntax = "v1"

info (
	title:   "api 文件完整示例写法"
	date:    "2025 年 7 月"
	version: "v1"
)

import "hello2.api" //使用相对路径,导入其它api文件

// g1
// 定义结构体
type (
	UpdateReq {
		Arg1 string `json:"arg1"`
	}
	ListItem {
		Value1 string `json:"value1"`
	}
	// 定义登陆接口的json请求体
	LoginReq {
		Username string `json:"username"`
		Password string `json:"password"`
	}
	// 定义登陆接口的json响应体
	LoginResp {
		Name string `json:"name"`
	}
	FormExampleReq {
		Name string `form:"name"`
	}
	PathExampleReq {
		// path 标签修饰的 id 必须与请求路由中的片段对应,如
		// id 在 service 语法块的请求路径上一定会有 :id 对应,见下文。
		ID string `path:"id"`
	}
	PathExampleResp {
		Name string `json:"name"`
	}
)

// g2
type (
	Base {
		Code int    `json:"code"`
		Msg  string `json:"msg"`
	}
	UserInfo {
		Id   int64  `json:"id"`
		Name string `json:"name"`
		Desc string `json:"desc"`
	}
	GetUserInfoReq {
		Id int64 `json:"id"`
	}
	Nested {
		Foo string `json:"foo"`
	}
	GetUserInfoResp {
		// api 支持结构体引用
		Base
		Data UserInfo `json:"data"`
		Nested Nested `json:"nested"`
	}
)

// 定义HTTP服务
// @server 语法块主要用于控制对 HTTP 服务生成时 meta 信息,目前支持功能有:
// 1. 路由分组
// 2. 中间件声明
// 3. 路由前缀
// 4. 超时配置
// 5. jwt 鉴权开关
// 所有声明仅对当前 service 中的路由有效
@server (
	jwt:    Auth // 对当前 Foo 语法块下的所有路由,开启 jwt 认证,不需要则请删除此行
	prefix: /v1 // 对当前 Foo 语法块下的所有路由,新增 /v1 路由前缀,不需要则请删除此行
	group:  g1 // 对当前 Foo 语法块下的所有路由,路由归并到 g1 目录下,不需要则请删除此行
	// 定义一个超时时长为 3 秒的超时配置,这里可填写为 time.Duration 的字符串形式,详情可参考
	// https://pkg.go.dev/time#Duration.String
	timeout: 3s // 对当前 Foo 语法块下的所有路由进行超时配置,不需要则请删除此行
	// 定义一个鉴权控制的中间件,多个中间件以英文逗号分割,如 Middleware1,Middleware2,中间件按声明顺序执行
	middleware: AuthInterceptor // 对当前 Foo 语法块下的所有路由添加中间件,只是生成代码框架,逻辑自己填写
	// 定义一个请求体限制在 1MB 以内的请求,goctl >= 1.5.0 版本支持
	maxBytes: 1048576 // 对当前 Foo 语法块下的所有路由添加请求体大小控制,单位为 byte,goctl 版本 >= 1.5.0 才支持
)
// 微服务名称为Foo
// 定义多个service代码块时,服务名称必须一致
service Foo {
	// 定义 http.HandleFunc 转换的go文件名称以及方法,每个接口都要有一个handler
	@handler pathExample
	// 定义方法为 get
	// 路由为/path/example/:id
	// 请求体为PathExampleReq
	// 响应体为PathExampleResp
	get /path/example/:id (PathExampleReq) returns (PathExampleResp)

	// 定义没有请求体和响应体的接口,如 ping
	@handler ping
	get /ping

	// 定义只有请求体的接口,如更新信息
	@handler update
	post /update (UpdateReq)

	// 定义只有响应体的结构,如获取全部信息列表
	@handler list
	get /list returns ([]ListItem)

	// 定义有结构体和响应体的接口,如登录
	@handler login
	post /login (LoginReq) returns (LoginResp)

	// 定义表单请求
	@handler formExample
	post /form/example (FormExampleReq)
}

@server (
	prefix: /v2 // 新增v2路由前缀
	group:  g2 // 当前语法块下的路由并到g2文件夹内
)
// 定义一个名称为 Foo 的服务
service Foo {
	// 定义 http.HandleFunc 转换的 go 文件名称及方法,每个接口都会跟一个 handler
	@handler getUserInfo
	// 定义接口
	// 请求方法为 post
	// 路由为 /user/info
	// 请求体为 GetUserInfoReq
	// 响应体为 GetUserInfoResp,响应体必须有 returns 关键字修饰
	post /user/info (GetUserInfoReq) returns (GetUserInfoResp)
}

这是引入的hello2.api的内容,多分一个文件只是为了展示引入功能。

// g3
type (
	SignDemoReq {
		Msg string `json:"msg"`
	}
	SignDemoResp {
		Msg string `json:"msg"`
	}
)

@server (
	signature:  true // 通过 signature 关键字开启签名功能
	prefix:     /v3 // 新增v3路由前缀
	group:      g3 // 当前语法块下的路由并到g3文件夹内
	timeout:    3s // 超时
	middleware: MyCustomizedMiddleware // 中间件
	maxBytes:   1048576 // 对当前 Foo 语法块下的所有路由添加请求体大小控制
)
service Foo {
	@handler SignDemo
	post /sign/demo (SignDemoReq) returns (SignDemoResp)
}

http 代码生成

通过命令goctl api go --api api_test/hello.api --dir hello_go --style goZero 生成go代码.
再通过命令表格中的所有命令尝试一下所有的代码生成,在生成前端代码和doc文档时遇到了些兼容性问题。生成完之后,代码架构如下:

tree
.
├── api_test # api文件
│   ├── hello.api
│   └── hello2.api
├── go.mod
├── go.sum
├── hello_dart # dart 客户端代码
│   └── data
│       ├── foo.dart
│       └── tokens.dart
├── hello_go # go 服务端代码
│   ├── etc
│   │   └── foo.yaml # 配置文件
│   ├── foo.go # 项目启动入口
│   └── internal
│       ├── config
│       │   └── config.go # 解析配置文件的结构体
│       ├── handler # 路由注册函数
│       │   ├── g1
│       │   │   ├── formExampleHandler.go
│       │   │   ├── listHandler.go
│       │   │   ├── loginHandler.go
│       │   │   ├── pathExampleHandler.go
│       │   │   ├── pingHandler.go
│       │   │   └── updateHandler.go
│       │   ├── g2
│       │   │   └── getUserInfoHandler.go
│       │   ├── g3
│       │   │   └── signDemoHandler.go
│       │   └── routes.go
│       ├── logic # 后端请求处理逻辑
│       │   ├── g1
│       │   │   ├── formExampleLogic.go
│       │   │   ├── listLogic.go
│       │   │   ├── loginLogic.go
│       │   │   ├── pathExampleLogic.go
│       │   │   ├── pingLogic.go
│       │   │   └── updateLogic.go
│       │   ├── g2
│       │   │   └── getUserInfoLogic.go
│       │   └── g3
│       │       └── signDemoLogic.go
│       ├── middleware # 中间件
│       │   ├── authinterceptorMiddleware.go
│       │   └── mycustomizedMiddleware.go
│       ├── svc # 请求处理上下文
│       │   └── serviceContext.go
│       └── types # 请求、响应结构体
│           └── types.go
├── hello_kt  # kotlin 客户端代码
│   └── BaseApi.kt
├── hello_swagger # swagger 文档代码
│   └── hello.json
├── hello_ts # typescript 客户端代码
│   ├── Foo.ts
│   ├── FooComponents.ts
│   └── gocliRequest.ts
├── hello.md # doc markdown文档
└── hello2.md # doc markdown 文档

在internal/logic/中,添加请求的响应逻辑。下面是一个简单的例子。

func (l *DemoLogic) Demo(req *types.Request) (resp *types.Response, err error) {
	log.Default().Printf("recv: %v\n", *req)
	resp = &types.Response{
		Message: fmt.Sprintf("now time: %v", time.Now()),
	}
	return
}

在补充完响应逻辑,修改完配置文件后。在hello_go文件夹下运行go run foo.go在8888端口启动服务器。

go run foo.go
Starting server at 0.0.0.0:8888...
{"@timestamp":"2025-07-04T19:10:21.890+08:00","caller":"proc/shutdown.go:84","content":"Got signal 2, shutting down...","level":"info"}
{"@timestamp":"2025-07-04T19:10:22.892+08:00","caller":"stat/metrics.go:210","content":"(Foo) - qps: 0.0/s, drops: 0, avg time: 0.0ms, med: 0.0ms, 90th: 0.0ms, 99th: 0.0ms, 99.9th: 0.0ms","level":"stat"}

rpc服务代码生成

rpc服务通过proto3版本的proto文件生成,常见命令用法如下:

命令用法说明
goctl rpcgoctl rpc [flags]生成 RPC 代码或输出示例 .proto 文件。--o <file>:输出示例 proto 文件(如 goctl rpc --o greet.proto)
goctl rpc newgoctl rpc new [flags]快速生成一个 RPC Demo。 --style <string>:文件命名格式,默认 gozero; --verbose:开启日志输出。
goctl rpc protocgoctl rpc protoc [flags] <your.proto> [protoc args]根据 .proto 文件生成 gRPC 代码及 zrpc 服务代码。-c, --client:是否生成客户端(默认 true); -m, --multiple:多服务模式;--zrpc_out <dir>:zrpc 输出目录(其它参数可与原生 protoc 混用)

单个 rpc 服务生成示例指令$ goctl rpc protoc greet.proto --go_out=./pb --go-grpc_out=./pb --zrpc_out=. --client=true
多个 rpc 服务生成示例指令$ goctl rpc protoc greet.proto --go_out=./pb --go-grpc_out=./pb --zrpc_out=. --client=true -m

一组功能相对完整的proto文件如下所示:
greet.proto文件,import似乎还有点问题,这边就把代码都写在同一文件了。

// 声明 proto 语法版本,固定值
syntax = "proto3";

// proto 包名
package greet;

// 生成 golang 代码后的包名
option go_package = "example/proto/greet";

// 定义结构体
message SendMessageReq{
  string message = 1;
}

// 定义枚举
enum Status{
  UNSPECIFIED = 0;
  SUCCESS = 1;
  FAILED = 2;
}

message Base{
  int32 code = 1;
  string msg = 2;
}

message SendMessage{
  // 使用枚举
  Status status = 1;
  // 数组
  repeated string array = 2;
  // map
  map<string,int32> map = 3;
  // 布尔类型
  bool boolean = 4;
  // 序列号保留
  reserved 5;
}

message SendMessageResp{
  Base base = 1;
  SendMessage data = 2;
}

message GetMessageReq{
  int32 id = 1;
}
message GetMessageResp{
  string message = 1;
}

// 定义 Greet 服务
service Greet {
  // 定义客户端流式 rpc
  rpc SendMessage(stream SendMessageReq) returns (SendMessageResp);
  // 定义服务端流式 rpc
  rpc GetMessage(GetMessageReq) returns (stream GetMessageResp);
  // 定义双向流式 rpc
  rpc PushMessage(stream SendMessageReq) returns (stream GetMessageResp);
}

// 定义 Message 服务
service Message {
  // 定义客户端流式 rpc
  rpc SendMessage(stream SendMessageReq) returns (SendMessageResp);
  // 定义服务端流式 rpc
  rpc GetMessage(GetMessageReq) returns (stream GetMessageResp);
  // 定义双向流式 rpc
  rpc PushMessage(stream SendMessageReq) returns (stream GetMessageResp);
}

生成的代码目录如下

tree
.
├── client                              # 客户端调用层,包含生成的 gRPC 客户端代码
│   ├── greet                           # greet 服务的客户端逻辑
│   │   └── greet.go                    # greet 客户端入口,封装了对服务端的 gRPC 调用
│   └── message                         # message 服务的客户端逻辑
│       └── message.go                  # message 客户端入口,封装了对服务端的 gRPC 调用
├── etc                                 # 配置文件目录
│   └── greet.yaml                      # greet 服务的 YAML 配置,如端口、日志等
├── greet.go                            # 服务启动入口(整合了 config、svc、server 等)
├── greet.proto                         # Protobuf 定义文件,描述 greet 服务的 RPC 接口
├── internal                            # 服务内部实现,业务逻辑与框架解耦
│   ├── config                          # 配置解析
│   │   └── config.go                   # 将 etc 中的 YAML 配置解析到 Go 结构体
│   ├── logic                           # 业务逻辑层,处理具体请求
│   │   ├── greet                       # greet 服务的逻辑实现
│   │   │   ├── getmessagelogic.go      
│   │   │   ├── pushmessagelogic.go     
│   │   │   └── sendmessagelogic.go    
│   │   └── message                     # message 服务的逻辑实现
│   │       ├── getmessagelogic.go      
│   │       ├── pushmessagelogic.go     
│   │       └── sendmessagelogic.go     
│   ├── server                          # gRPC 服务器注册层
│   │   ├── greet                       # greet 服务的服务端实现
│   │   │   └── greetserver.go          # 实现了 Protobuf 生成的 greet 服务接口,调用对应 logic
│   │   └── message                     # message 服务的服务端实现
│   │       └── messageserver.go        # 实现了 Protobuf 生成的 message 服务接口,调用对应 logic
│   └── svc                             # 依赖注入与上下文
│       └── servicecontext.go           # hold 登录、DB、RPC 客户端等外部依赖的上下文
└── pb                                  # Protobuf 生成的 Go 代码
    └── example
        └── proto
            └── greet
                ├── greet_grpc.pb.go    # gRPC 服务接口与客户端存根(stub)生成代码
                └── greet.pb.go         # Protobuf 消息类型的生成代码

修改配置文件如下后,就能通过go run greet.go启动了。

Name: greet.rpc
ListenOn: 0.0.0.0:8080
Mode: dev

数据库代码生成

goctl model 为 goctl 提供的数据库模型代码生成指令,目前支持 MySQL、PostgreSQL、Mongo 的代码生成,MySQL 支持从 sql 文件和数据库连接两种方式生成,PostgreSQL 仅支持从数据库连接生成。

MySQL

goctl model mysql 指令用于生成基于 MySQL 的 model 代码,支持生成带缓存和不带缓存的代码。MySQL 代码生成支持从 sql 文件,数据库连接两个来源生成代码。
mysql 生成的代码内容有数据表对应的 golang 结构体、CURD 操作方法,缓存逻辑等信息。
首先编写建表sql,官方的例子如下:

CREATE TABLE user (
    id bigint AUTO_INCREMENT,
    name varchar(255) NULL COMMENT 'The username',
    password varchar(255) NOT NULL DEFAULT '' COMMENT 'The user password',
    mobile varchar(255) NOT NULL DEFAULT '' COMMENT 'The mobile phone number',
    gender char(10) NOT NULL DEFAULT 'male' COMMENT 'gender,male|female|unknown',
    nickname varchar(255) NULL DEFAULT '' COMMENT 'The nickname',
    type tinyint(1) NULL DEFAULT 0 COMMENT 'The user type, 0:normal,1:vip, for test golang keyword',
    create_at timestamp NULL,
    update_at timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    UNIQUE mobile_index (mobile),
    UNIQUE name_index (name),
    PRIMARY KEY (id)
) ENGINE = InnoDB COLLATE utf8mb4_general_ci COMMENT 'user table';

运行goctl model mysql ddl --src user.sql --dir .即可生成该表的常用CURD代码。
生成代码结构如下:

❯ tree
.
├── user.sql
├── usermodel_gen.go
├── usermodel.go
└── vars.go

其中:

  • user.sql是我们的建表sql语句源文件
  • usermodel_gen.go是结构体以及常见的CURD代码,我理解这个文件在正常情况下是不用修改的。
  • vars.go 是公共变量文件,默认内容有一个自定义的error变量
  • usermodel.go 中是需要修改的代码,为实现自定义查询等提供了支持,下面将对该文件的代码进行分析。
MySQL自定义业务实现

这边对usermodel.go内容进行分析,然后给出一个自定义的方法实现。

package mysql

import (
	"context"
	"fmt"

	"github.com/zeromicro/go-zero/core/stores/sqlx"
)

// Go 语言中一种​​编译期接口实现检查​​的惯用写法,其核心目的是确保 customUserModel 结构体完整实现了 UserModel 接口的所有方法。
var _ UserModel = (*customUserModel)(nil)

type (
	// UserModel is an interface to be customized, add more methods here,
	// and implement the added methods in customUserModel.
	UserModel interface {
		userModel                                                           // usermodel_gen.go 中定义的接口,里面包含了基础的CURD方法
		withSession(session sqlx.Session) UserModel                         // 默认生成的方法,可以在下面自定义实现
		FindMoreByGender(ctx context.Context, name string) (*[]User, error) // 自定义的查询方法
	}

	customUserModel struct {
		*defaultUserModel //
	}
)

// 返回一个UserModel实例,能够对数据库实现增删改查
func NewUserModel(conn sqlx.SqlConn) UserModel {
	return &customUserModel{
		defaultUserModel: newUserModel(conn),
	}
}

// withSession 生成一个带session的连接,确保多个数据库操作在同一个事务中执行
func (m *customUserModel) withSession(session sqlx.Session) UserModel {
	return NewUserModel(sqlx.NewSqlConnFromSession(session))
}

// FindMoreByName 实现了一个自定义查询
func (m *customUserModel) FindMoreByGender(ctx context.Context, gender string) (*[]User, error) {
	var resp []User
	query := fmt.Sprintf("select %s from %s where `gender` = ?", userRows, m.table)
	err := m.conn.QueryRowsCtx(ctx, &resp, query, gender)
	switch err {
	case nil:
		return &resp, nil
	case sqlx.ErrNotFound:
		return nil, ErrNotFound
	default:
		return nil, err
	}
}

此时,在后端逻辑中调用该方法,如:

func (l *FindUserLogic) FindUser(req *types.FindUserRequest) (resp *types.Response, err error) {
	// 这边略微修改下api文件,添加这个方法
	gender := req.Gender
	// 这个需要自行修改servicecontext.go文件,添加从配置文件中获取和建立数据库连接的过程。
	userModel := l.svcCtx.UserModel
	// 这边调用自定义的方法
	users, err := userModel.FindMoreByGender(context.Background(), gender)
	if err != nil {
		resp = &types.Response{
			Message: err.Error(),
		}
		return
	}
	res := []string{}
	for _, user := range *users {
		res = append(res, user.Name.String)
	}
	log.Default().Println(res)
	resp = &types.Response{
		Message: fmt.Sprintf("%#v", res),
	}
	return
}

MongoDB

通过如下命令生成模版代码,该例子生成了Collection名称为User的mongo代码。
goctl model mongo --type user --dir mongo
生成的代码结构如下:

.
├── error.go
├── usermodel.go
├── usermodelgen.go
└── usertypes.go

在usertypes.go中,添加user的属性。

type User struct {
	ID       primitive.ObjectID `bson:"_id,omitempty" json:"id,omitempty"`
	Name     string             `bson:"name,omitempty" json:"name,omitempty"`
	UpdateAt time.Time          `bson:"updateAt,omitempty" json:"updateAt,omitempty"`
	CreateAt time.Time          `bson:"createAt,omitempty" json:"createAt,omitempty"`
}
MongoDB自定义业务实现

在usermodel.go中,添加自定义的方法实现。

package mongo

import (
	"context"
	"fmt" // For string formatting
	// For random number generation
	"time" // For timestamps

	"github.com/zeromicro/go-zero/core/stores/mon"
	"go.mongodb.org/mongo-driver/bson"
)

var _ UserModel = (*customUserModel)(nil)

type (
	UserModel interface {
		userModel
		FindOneByName(ctx context.Context, name string) (*User, error) // 自定义的方法
		GenUserByName(ctx context.Context, name string) (*User, error) // 自定义的方法
	}

	customUserModel struct {
		*defaultUserModel
	}
)

// NewUserModel returns a model for the mongo.
func NewUserModel(url, db, collection string) UserModel {
	conn := mon.MustNewModel(url, db, collection)
	return &customUserModel{
		defaultUserModel: newDefaultUserModel(conn),
	}
}

// FindOneByName finds a user by their name.
func (u *customUserModel) FindOneByName(ctx context.Context, name string) (*User, error) {
	var user User
	filter := bson.M{"name": name}

	err := u.conn.FindOne(ctx, &user, filter)
	if err != nil {
		if err == mon.ErrNotFound {
			return nil, nil
		}
		return nil, err
	}

	return &user, nil
}

// GenRandomUser generates a new user with a random name and inserts them into the database.
func (u *customUserModel) GenUserByName(ctx context.Context, name string) (*User, error) {

	now := time.Now()

	newUser := &User{
		Name:     name,
		CreateAt: now,
		UpdateAt: now,
	}

	// Insert the new user into the database
	_, err := u.conn.InsertOne(ctx, newUser)
	if err != nil {
		return nil, fmt.Errorf("failed to insert random user: %w", err)
	}

	// After successful insertion, newUser should have its ID populated
	return newUser, nil
}

数据库配置文件

依据之前生成的demo项目,修改etc/demo-api.yaml,加入配置

Name: demo-api
Host: 0.0.0.0
Port: 8888

Mysql:
  DataSource: root:123456@tcp(localhost:3306)/micro?charset=utf8mb4&parseTime=true&loc=Local

Mongo:
  DataSource: mongodb://root:123456@127.0.0.1:27017

同步修改internal/config/config.go中的结构

package config

import "github.com/zeromicro/go-zero/rest"

type Config struct {
	rest.RestConf

	// MySQL 配置
	Mysql struct {
		DataSource string
	}

	// Mongo 配置
	Mongo struct {
		DataSource string
	}
}

然后在 internal/svc/servicecontext.go中,注册对应连接后,即可在逻辑文件中,调用连接并进行CURD。

package svc

import (
	"hello/demo/internal/config"

	"hello/demo/internal/model/mongo"
	"hello/demo/internal/model/mysql"

	"github.com/zeromicro/go-zero/core/stores/sqlx"
)

type ServiceContext struct {
	Config    config.Config
	SqlConn   sqlx.SqlConn    // 全局Mysql数据库连接
	UserMongo mongo.UserModel // 接口是后续生成的
	UserModel mysql.UserModel // 接口是后续生成的
}

func NewServiceContext(c config.Config) *ServiceContext {
	conn := sqlx.NewMysql(c.Mysql.DataSource)
	userMongo := mongo.NewUserModel(c.Mongo.DataSource, "micro", "users")
	return &ServiceContext{
		Config:    c,
		SqlConn:   conn, // 保持长连接
		UserModel: mysql.NewUserModel(conn),
		UserMongo: userMongo,
	}
}

添加用户例子,这是internal/logic/adduserlogic.go文件中的内容。

package logic

import (
	"context"
	"database/sql"
	"fmt"
	"time"

	"hello/demo/internal/model/mysql"
	"hello/demo/internal/svc"
	"hello/demo/internal/types"

	"github.com/zeromicro/go-zero/core/logx"
)

type AddUserLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewAddUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AddUserLogic {
	return &AddUserLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *AddUserLogic) AddUser(req *types.Request) (resp *types.Response, err error) {
	name := req.Name
	userModel := l.svcCtx.UserModel
	_, err = userModel.Insert(context.Background(), &mysql.User{
		Name: sql.NullString{
			String: name,
			Valid:  true,
		},
		Password: "pswd",
		Mobile:   fmt.Sprintf("%v", time.Now()),
		Gender:   "male",
		Nickname: "nic-" + name,
		Type:     0,
	})
	if err != nil {
		resp = &types.Response{
			Message: "fail" + err.Error(),
		}
		return
	}

	// add to mongo
	userMongo := l.svcCtx.UserMongo
	_, err = userMongo.GenUserByName(context.Background(), name)
	if err != nil {
		resp = &types.Response{
			Message: "fail" + err.Error(),
		}
		return
	}
	resp = &types.Response{
		Message: "success",
	}
	return
}
go-zero单体应用入门实战(一) go-zero是一个go语言的框架,类似于java中的spring boot和spring cloud。作者是国内go语言大神级人物,万俊峰Kevin。go-zero功能非常强大,初学者建议从单体应用开始,再逐步深入。 阅读详情

相关推荐

go-zero学习 — 进阶

go-zero学习 — 进阶

Mr_XiMu的博客 2885

go 进阶 go-zero相关: 一. go-zero 基础

├── etc│ └── core-api.yaml // 配置文件├── go.mod // mod文件├── greet.api // api描述文件├── core.go // main函数入口├── config│ └── config.go // 配置声明type├── handler // 路由及handler转发├── logic // 业务逻辑├── middleware // 中间件文件├── svc // logic所依赖的资源池。

qq_29799655的博客 4689

【保姆级教程】Windows11安装go-zero代码生成工具goctl、protoc、go-zero

【保姆级教程】Windows11安装go-zero代码生成工具goctl、protoc、go-zero 一文带你Windows11安装go-zero代码生成工具goctl、protoc、go-zero,全程手把手搭建环境!

寸铁的博客 3791

go-zero框架快速入门

go-zero 是一个集成了各种工程实践的 web 和 rpc 框架。通过弹性设计保障了大并发服务端的稳定性,经受了充分的实战检验。go-zero 包含极简的 API 定义和生成工具 goctl,可以根据定义的 api 文件一键生成代码。

码农兴哥的技术笔记 3742

【Go-Zero】goctl一键代码生成常用实战命令

【Go-Zero】goctl一键代码生成常用实战命令 goctl 是 go-zero的内置脚手架,是提升开发效率的一大利器,可以一键生成代码、文档、部署k8s、yaml、dockerfile 等。 本文主要是针对企业实战最常用的api、model、rpc代码生成的命令使用和参数说明的详细描述!

寸铁的博客 3669

我用go-zero开发了第一个线上项目

作者:结冰 前言 ​ 说在最前面,我是一个外表谦让,内心狂热,外表斯文,内心贪玩的一个普通人。我的职业是程序员,是一个golang语言爱好者,一半是因为golang好用,一半是因为其他语言学不好。我是从phper转为gopher的,写php的时候我认识了互联网软件,写go的时候感觉自己终于在编程。 初见golang ​ 我大学专业是软件。第一门编程语言是C++,知道了指针,知道了加减乘除,知道了编程去控制软硬件。后来选修了java,被ssh框架戏耍了一个暑假。再后来进入了一个社团技术部,再被ht

我心飞翔 3297

笔记:go-zero框架

点击新建添加%GOROOT%\bin,(这里使用%GOROOT%是为了动态应用GOROOT的值,也就是之前在用户变量中新建的D:\go\go,如果之后验证没有成功,之前的操作也没问题可以把这里的%GOROOT%\bin换成D:\go\go\bin),添加完成之后,一路点击确定,直到桌面。我配置两个GOPATH目录,一个用于放 Go 语言的第三方包,另一个用于放自己的开发代码我这里已经添加过了,没有的话自行添加。goctl 是 go-zero 的内置脚手架,是提升开发效率的一大利器,可以一键生成代码、文档。

m0_71060733的博客 1928

Go-Zero微服务快速入门和最佳实践(一)

这篇文章带你梳理了使用gozero开发微服务项目的步骤和技巧,请你按我建议的方式开发和debug,会很清晰。下一篇文章将通过一个完整的功能,带你跑通一个微服务的开发,包括:需求分析+表结构设计+api+rpc+goctl+apifox调试+细节处理。如果你对Go语言或者微服务感兴趣,欢迎关注我的公众号:王中阳Go,也欢迎直接私信我。

王中阳讲编程 2528

《go-zero云笺密钥录》

本文介绍了如何在go-zero框架下实现一个生成随机数字验证码的api网络接口

麦麦的博客 3281

go-zero微服务入门教程

《Go-zero 微服务开发完全指南》摘要 本文是一份全面的Go-zero微服务开发指南,覆盖1.6.4+版本,提供从入门到进阶的系统学习路径。

qq_48107900的博客 2206

go-zero入门

go-zero 是一个集成了各种工程实践的 web 和 rpc 框架。通过弹性设计保障了大并发服务端的稳定性,经受了充分的实战检验。go-zero 包含极简的 API 定义和生成工具 goctl,可以根据定义的 api 文件一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码,并可直接运行。强大的工具支持,尽可能少的代码编写极简的接口完全兼容 net/http支持中间件,方便扩展高性能面向故障编程,弹性设计。

苏叶新城 2899

go-zero微服务框架代码生成神器goctl原理分析(一)

推荐下go-zero 微服务框架,也是最近很火很有人气的框架,致力于打造国内最简单好用的框架。 火在哪?几分钟搞定个接口和微服务,还不用担心稳定性和高并发,这不香吗? 作者的理念很好,工具大于约定和文档的理念,编码自动化,让精力更多的放在业务和创新上,大幅度的提高效率和生产力,这会是个趋势。 golang圈子不大,微服务框架框架屈指可数:除了go-micro、go-kit,几乎没有其他选择。go-zero为此提供第三个可能。 说起go-zero提高生产力的地方,就不得不说goctl。 goctl是

猫哥 的沉淀、积累、总结。天天学习,好好向上...c/c++,嵌入式 linux,Android,HarmonyOS,AIOT) 9303

Mamba-YOLO实战:环境配置+模型训练+精度调优全流程指南(附Colab代码)

在目标检测领域,一场由状态空间模型引发的技术革新正在悄然改写行业格局。近期国际顶级学术期刊连续发布的YOLOv5_mamba、FER-YOLO等突破性成果,不仅刷新了检测精度记录,更开创了结构化状态空间模型(SSM)与经典检测框架的创新融合范式。当前该领域涌现的十大标杆性研究,已在以下维度取得突破性进展:√ 遮挡目标识别准确率提升38%√ 小样本场景泛化能力增强2.7倍√ 视频流检测延迟降低至17ms√ 多目标交叉干扰抑制效果达SOTA。

2501_90555291的博客 1583

[go-zero] goctl 生成api和rpc

gozero中生成api和rpc接口代码

959 1462

【大唐杯学习超快速入门】5G技术原理仿真教学——5G网络开通调测与车联网

自用笔记,比较乱。

qq_41224270的博客 1万+

蛋白质组学实战指南 | (八)从数据到洞见:差异蛋白筛选与功能解析全流程

本文系统阐述了蛋白质组学数据分析中,从原始数据质控到差异蛋白筛选与功能解析的全流程。重点介绍了数据质量评估、差异蛋白筛选的统计方法(包括多重检验校正)、功能注释与富集分析,以及蛋白互作网络构建与关键节点识别。最后,为如何将生物信息学洞见转化为实验验证提供了清晰的规划思路,助力研究者完成从数据到生物学知识的闭环。

weixin_29250573的博客 372

window10下go语言开发环境搭建,及体验go-zero框架,及解决command not found: goctl

前言 go-zero 是一个集成了各种工程实践的 web 和 rpc 框架。通过弹性设计保障了大并发服务端的稳定性,经受了充分的实战检验。 go-zero 包含极简的 API 定义和生成工具 goctl,可以根据定义的 api 文件一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码,并可直接运行。 这么牛,怎么不想去试一下?! go语言环境搭建 配置GO语言环境 新增go的代理GOPROXY=https://gopr.

阿啄debugIT 5922

ArcGIS实战:地形湿度指数(TWI)计算全流程解析

本文详细解析了ArcGIS中地形湿度指数(TWI)的计算全流程,从DEM数据获取、填洼处理到水流方向与汇流累积量计算,最终实现TWI的精准计算。通过实战步骤指导,帮助读者掌握水文建模和土壤湿度预测的关键技术,适用于农业规划、生态研究等领域。

weixin_29231725的博客 871

自go-zero走进微服务

Go-zero 的微服务进阶之路:始于 goctl 的契约驱动与提效,立于 zrpc 与 etcd 的架构互联,成于 熔断限流等全链路服务治理的坚实保障。

2302_80067378的博客 2471

Agilent N5230A矢量网络分析仪在射频器件S参数测试中的关键操作指南

本文详细介绍了Agilent N5230A矢量网络分析仪在射频器件S参数测试中的关键操作流程与技巧。内容涵盖从开机预热、校准(SOLT/ECal)、参数设置(频率、点数、功率、IFBW)到连接测量、数据保存分析的全过程,并分享了夹具去嵌入、时域分析等进阶功能与常见问题避坑指南,旨在帮助工程师获得精准可靠的散射参数测试结果。

kotlin6android的博客 402

pe卸载win10更新补丁_禁用Win10系统更新

众所周知,最近WIN10的强制更新导致了很多问题。如:(部分专业软件系统更新后无法使用,硬件中键盘、鼠标、指纹之类的硬件,更新补丁后失灵等问题)。而之前的禁用WindowsUpdate的方法已经失效。下面我们另辟蹊径,利用系统权限的方法使Win10无法更新,达到禁用更新的目的。1.计算机管理-服务中禁用Windows Update服务。2.打开C:\Windows\softwareDistribu...

weixin_39702479的博客 1万+

【Go-Zero】goctl生成model层后报错Unresolved reference ‘ErrNotFound‘解决方案

【Go-Zero】goctl生成model层后报错Unresolved reference 'ErrNotFound'解决方案

寸铁的博客 1800

ESP32S3 Korvo2V3四通道AEC配置实战:基于AFE的回声消除优化

本文详细介绍了在ESP32S3 Korvo2V3平台上配置四通道AEC(回声消除)的实战经验。通过AFE算法优化和硬件连接调试,实现高质量回声消除,显著提升智能音箱和会议设备的语音交互体验。重点解析了四通道配置、数据格式对齐和参数调优等关键技术要点。

elastic6hunter的博客 938

Go-Zero 框架使用 MongoDB,数据采集入库如此简单

以上完成实现了采集知乎日报的新闻列表信息,采集入mongoDB数据库。使用了go-zero 的goctl工具自动生成操作mongoDB的代码,使用了go-zero框架中的部分模块如日志模块,配置文件操作模块、网络访问模块等。可以看到借助goctl自动生成代码,采集数据入mongoDB数据库是多么的简单方便。再次推荐下go-zero这一优秀的微服务框架。

猫哥 的沉淀、积累、总结。天天学习,好好向上...c/c++,嵌入式 linux,Android,HarmonyOS,AIOT) 1762

打开 AI 投研新时代 —— 深度解析 GitHub 高星项目 daily_stock_analysis

GitHub热门开源项目"daily_stock_analysis"是一款AI驱动的智能股票分析工具,能自动抓取行情数据、分析新闻语义并生成结构化报告。该项目采用Python开发,通过GitHub Actions实现全自动运行,支持邮件、企业微信等多渠道推送。核心功能包括AI决策仪表盘、多数据源整合和零成本部署方案,特别适合需要自动化投研辅助的个人投资者。项目强调其仅为分析工具而非投资建议,提醒用户注意AI模型的潜在误差。该工具通过智能化处理重复工作,帮助投资者提升决策效率,成为量化投资

陈奕昆的博客 1527

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

Go-zero 安装与使用(详细流程)

go-zero 是一个集成了:API 网关 + RPC + 中间件 + 代码生成 + 微服务治理 的 Go 微服务框架。 2. 安装 goctl 工具 3. 验证安装 如果报错:说明环境变量未配置,按以下步骤解决: Windows 解决方案 项目创建与目录结构 1. 创建项目目录 2. 使用 goctl 创建 API 项目 3. 生成后的标准目录结构 4. 目录结构说明(重要) 目录/文件 作用 说明 接口定义 定义所有 API 接口,goctl 根据此文件自动生成代码

卡尔特斯 889

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

上一篇: 【Golang】Gin框架中间件学习笔记
Crisp0v0
博客等级 码龄7年 124粉丝 20原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值