目录
前言
在学习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 rpc | goctl rpc [flags] | 生成 RPC 代码或输出示例 .proto 文件。--o <file>:输出示例 proto 文件(如 goctl rpc --o greet.proto) |
| goctl rpc new | goctl rpc new [flags] | 快速生成一个 RPC Demo。 --style <string>:文件命名格式,默认 gozero; --verbose:开启日志输出。 |
| goctl rpc protoc | goctl 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
}
2885



被折叠的 条评论
为什么被折叠?



