go-zero项目准备
创建module
go mod init afs
拉取go-zero项目
go get -u github.com/zeromicro/go-zero@latest
创建api文件
#cd path/api
#vim user.api
#以下为文件内容
syntax = "v1" // 标记 api 语言的版本
// 接口请求参数 - 通过form请求接收uid参数
type UserInfoRequest {
Uid int64 `form:"uid"`
}
// 接口相应参数
type UserInfoResponse {
Status int `json:"status"`
Message string `json:"message"`
Data map[string]interface{} `json:"data"`
}
// 定义一个http服务 - get请求/user/info
// 服务接收参数 - UserInfoRequest
// 服务响应参数 - UserInfoResponse
service afs-api {
/**
* @handler - 语句是对单个路由的 handler 信息控制
*/
@handler UserInfoHandler
get /user/info (UserInfoRequest) returns (UserInfoResponse)
}
goctl初始化go-zero项目
goctl api go --api ./api/user.api --dir .
修改项目配置文件
修改公共配置文件
#vim path/etc/afs-api.yaml
App:
Name: afs-api
Host: 0.0.0.0
Port: 8888
修改配置文件结构体
#vim path/internal/config/config.go
package config
import "github.com/zeromicro/go-zero/rest"
type Config struct {
App rest.RestConf
}
修改入口文件
#vim path/afs.go
package main
import (
"flag"
"fmt"
"afs/internal/config"
"afs/internal/handler"
"afs/internal/svc"
"github.com/zeromicro/go-zero/core/conf"
"github.com/zeromicro/go-zero/rest"
)
// 接收命令行中 -f 参数
var configFile = flag.String("f", "etc/afs-api.yaml", "the config file")
func main() {
// 解析命令行
flag.Parse()
// 加载配置
var c config.Config
conf.MustLoad(*configFile, &c)
// 注册server配置 - 修改行
server := rest.MustNewServer(c.App)
defer server.Stop()
// 初始化项目 - 全局变量、路由等
ctx := svc.NewServiceContext(c)
handler.RegisterHandlers(server, ctx)
// 启动服务 - 修改行
fmt.Printf("Starting server at %s:%d...\n", c.App.Host, c.App.Port)
server.Start()
}
下载依赖包
go mod tidy
启动项目
go run afs.go
增加mysql链接
表结构
CREATE TABLE `afs`.`afs_user`(
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '用户id',
`userid` VARCHAR(100) NOT NULL DEFAULT '' COMMENT '用户名',
`password` CHAR(32) NOT NULL DEFAULT '' COMMENT '密码',
`header` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '头像',
`real_name` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '真实姓名',
`mobile` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '电话',
`cash` DECIMAL(12,2) NOT NULL DEFAULT 0.00 COMMENT '可用余额',
`frozen_cash` DECIMAL(12,2) NOT NULL DEFAULT 0.00 COMMENT '冻结资金',
`is_lock` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否锁定',
`zt` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '用户状态0普通用户',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '添加时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
PRIMARY KEY (`id`),
INDEX `idx_userid` (`userid`),
INDEX `idx_mobile` (`mobile`)
)
COMMENT='用户表';
使用goctl生成相关文件
goctl model mysql datasource -url="用户名:密码@tcp(数据库地址:端口号)/数据库名" --table tableName --dir ./internal/model
修改公共配置文件 - 增加mysql配置
#vim path/etc/afs-api.yaml
App:
Name: afs-api
Host: 0.0.0.0
Port: 8888
Mysql:
afs:
Host: 127.0.0.1
Port: 3306
User: root
Pass: pass
Dbname: afs
OutTime: 60
MaxTime: 3600
MaxIdle: 2
MaxOpen: 10
修改配置文件结构体 - 增加mysql配置
#vim path/internal/config/config.go
package config
import "github.com/zeromicro/go-zero/rest"
type Config struct {
App rest.RestConf
Mysql map[string]struct{
Host string
Port int
User string
Pass string
Dbname string
OutTime int
MaxTime int
MaxIdle int
MaxOpen int
}
}
修改服务初始化文件 - 初始化mysql链接
#vim path/internal/svc/servicecontext.go
package svc
import (
"fmt"
"time"
"afs/internal/config"
"github.com/zeromicro/go-zero/core/stores/sqlx"
)
type ServiceContext struct {
Config config.Config
Mysql map[string]sqlx.SqlConn
}
func NewServiceContext(c config.Config) *ServiceContext {
// 全局变量
svc := ServiceContext{
Config: c,
}
//初始化mysql
svc.initMysql();
return &svc;
}
// 初始化mysql
func (svc *ServiceContext) initMysql(){
// 构造mysql
svc.Mysql = make(map[string]sqlx.SqlConn);
// 初始化链接
for k, v := range svc.Config.Mysql {
dsn := fmt.Sprintf("%v:%v@tcp(%v:%v)/%v?charset=utf8mb4&parseTime=True&loc=Local",v.User,v.Pass,v.Host,v.Port,v.Dbname);
conn := sqlx.NewMysql(dsn);
// 获取数据库句柄
db,err := conn.RawDB()
if err != nil{
fmt.Println(k + " 数据库链接失败")
}else{
fmt.Println(k + " 数据库链接成功")
// 设置连接池信息
db.SetConnMaxIdleTime(time.Duration(v.OutTime) * time.Second) // SetConnMaxIdleTime() - 最大空闲时间
db.SetConnMaxLifetime(time.Duration(v.MaxTime) * time.Second) // SetConnMaxLifetime() - 最大可用时间
db.SetMaxIdleConns(v.MaxIdle) // SetMaxIdleConns() - 最大空闲链接数
db.SetMaxOpenConns(v.MaxOpen) // SetMaxIdleConns() - 最大链接数
}
svc.Mysql[k] = conn
}
}
修改afs_user表Model文件
#vim internal/model/afsusermodel.go
package model
import (
"fmt"
"context"
"github.com/zeromicro/go-zero/core/stores/sqlx"
)
var _ AfsUserModel = (*customAfsUserModel)(nil)
type (
// AfsUserModel is an interface to be customized, add more methods here,
// and implement the added methods in customAfsUserModel.
AfsUserModel interface {
afsUserModel
withSession(session sqlx.Session) AfsUserModel
GetInfoById(ctx context.Context, id int64) (*AfsUser, error)
}
customAfsUserModel struct {
*defaultAfsUserModel
}
)
// NewAfsUserModel returns a model for the database table.
func NewAfsUserModel(conn sqlx.SqlConn) AfsUserModel {
return &customAfsUserModel{
defaultAfsUserModel: newAfsUserModel(conn),
}
}
func (m *customAfsUserModel) withSession(session sqlx.Session) AfsUserModel {
return NewAfsUserModel(sqlx.NewSqlConnFromSession(session))
}
// 增加方法 - 通过用户id获取用户信息
func (m *customAfsUserModel) GetInfoById(ctx context.Context, id int64) (*AfsUser, error){
var info AfsUser;
sql := fmt.Sprintf("SELECT * FROM afs_user WHERE id = %v limit 1",id);
err := m.conn.QueryRowCtx(context.Background(), &info, sql)
if err == nil{
return &info,nil;
}else{
return nil,err;
}
}
修改afs_user表Model_gen文件
#vim internal/model/afsusermodel_gen.go
// Code generated by goctl. DO NOT EDIT.
package model
import (
"context"
"database/sql"
"fmt"
"strings"
"time"
"github.com/zeromicro/go-zero/core/stores/builder"
// "github.com/zeromicro/go-zero/core/stores/sqlc" // 移除行
"github.com/zeromicro/go-zero/core/stores/sqlx"
"github.com/zeromicro/go-zero/core/stringx"
)
var (
afsUserFieldNames = builder.RawFieldNames(&AfsUser{})
afsUserRows = strings.Join(afsUserFieldNames, ",")
afsUserRowsExpectAutoSet = strings.Join(stringx.Remove(afsUserFieldNames, "`id`", "`create_at`", "`create_time`", "`created_at`", "`update_at`", "`update_time`", "`updated_at`"), ",")
afsUserRowsWithPlaceHolder = strings.Join(stringx.Remove(afsUserFieldNames, "`id`", "`create_at`", "`create_time`", "`created_at`", "`update_at`", "`update_time`", "`updated_at`"), "=?,") + "=?"
)
type (
afsUserModel interface {
Insert(ctx context.Context, data *AfsUser) (sql.Result, error)
FindOne(ctx context.Context, id int64) (*AfsUser, error)
Update(ctx context.Context, data *AfsUser) error
Delete(ctx context.Context, id int64) error
}
defaultAfsUserModel struct {
conn sqlx.SqlConn
table string
}
AfsUser struct {
Id int64 `db:"id"` // 用户id
Userid string `db:"userid"` // 用户名
Password string `db:"password"` // 密码
Header string `db:"header"` // 头像
RealName string `db:"real_name"` // 真实姓名
Mobile string `db:"mobile"` // 电话
Cash float64 `db:"cash"` // 可用余额
FrozenCash float64 `db:"frozen_cash"` // 冻结资金
IsLock int64 `db:"is_lock"` // 是否锁定
Zt int64 `db:"zt"` // 用户状态0普通用户
CreateTime time.Time `db:"create_time"` // 添加时间
UpdateTime time.Time `db:"update_time"` // 修改时间
}
)
func newAfsUserModel(conn sqlx.SqlConn) *defaultAfsUserModel {
return &defaultAfsUserModel{
conn: conn,
table: "`afs_user`",
}
}
func (m *defaultAfsUserModel) Delete(ctx context.Context, id int64) error {
query := fmt.Sprintf("delete from %s where `id` = ?", m.table)
_, err := m.conn.ExecCtx(ctx, query, id)
return err
}
func (m *defaultAfsUserModel) FindOne(ctx context.Context, id int64) (*AfsUser, error) {
query := fmt.Sprintf("select %s from %s where `id` = ? limit 1", afsUserRows, m.table)
var resp AfsUser
err := m.conn.QueryRowCtx(ctx, &resp, query, id)
switch err {
case nil:
return &resp, nil
case sqlx.ErrNotFound:
return nil, ErrNotFound
default:
return nil, err
}
}
func (m *defaultAfsUserModel) Insert(ctx context.Context, data *AfsUser) (sql.Result, error) {
query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?, ?, ?)", m.table, afsUserRowsExpectAutoSet)
ret, err := m.conn.ExecCtx(ctx, query, data.Userid, data.Password, data.Header, data.RealName, data.Mobile, data.Cash, data.FrozenCash, data.IsLock, data.Zt)
return ret, err
}
func (m *defaultAfsUserModel) Update(ctx context.Context, data *AfsUser) error {
query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, afsUserRowsWithPlaceHolder)
_, err := m.conn.ExecCtx(ctx, query, data.Userid, data.Password, data.Header, data.RealName, data.Mobile, data.Cash, data.FrozenCash, data.IsLock, data.Zt, data.Id)
return err
}
func (m *defaultAfsUserModel) tableName() string {
return m.table
}
修改逻辑层数据 - 从数据库中查询用户信息
#vim internal/logic/userinfologic.go
package logic
import (
"context"
"fmt"
"afs/internal/svc"
"afs/internal/types"
"afs/internal/model"
"github.com/zeromicro/go-zero/core/logx"
)
type UserInfoLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewUserInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserInfoLogic {
return &UserInfoLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *UserInfoLogic) UserInfo(req *types.UserInfoRequest) (resp *types.UserInfoResponse, err error) {
// todo: add your logic here and delete this line
resp = new(types.UserInfoResponse);
info,err := model.NewAfsUserModel(l.svcCtx.Mysql["afs"]).GetInfoById(l.ctx,req.Uid);
if err != nil{
resp.Status = 0;
resp.Message = "用户查询失败";
}else{
resp.Status = 1;
resp.Message = "ok";
resp.Data = make(map[string]interface{})
resp.Data["info"] = info;
}
fmt.Printf("%+v",info)
return
}
下载依赖包
go mod tidy
启动项目
go run afs.go
增加redis链接
修改公共配置文件 - 增加redis配置
#vim path/etc/afs-api.yaml
App:
Name: afs-api
Host: 0.0.0.0
Port: 8888
Mysql:
afs:
Host: 127.0.0.1
Port: 3306
User: root
Pass: pass
Dbname: afs
OutTime: 60
MaxTime: 3600
MaxIdle: 2
MaxOpen: 10
Redis:
aliyun:
Host: 127.0.0.1
Port: 6379
Pass:
Db: 0
OutTime: 60
MaxIdle: 2
MaxOpen: 10
修改配置文件结构体 - 增加redis配置
#vim path/internal/config/config.go
package config
import "github.com/zeromicro/go-zero/rest"
type Config struct {
App rest.RestConf
Mysql map[string]struct{
Host string
Port int
User string
Pass string
Dbname string
OutTime int
MaxTime int
MaxIdle int
MaxOpen int
}
Redis map[string]struct{
Host string
Port int
Pass string
Db int
OutTime int
MaxIdle int
MaxOpen int
}
}
修改服务初始化文件 - 初始化redis链接
#vim path/internal/svc/servicecontext.go
package svc
import (
"fmt"
"time"
"context"
"afs/internal/config"
"github.com/zeromicro/go-zero/core/stores/sqlx"
"github.com/go-redis/redis/v8"
)
type ServiceContext struct {
Config config.Config
Mysql map[string]sqlx.SqlConn
Redis map[string]*redis.Client
}
func NewServiceContext(c config.Config) *ServiceContext {
// 全局变量
svc := ServiceContext{
Config: c,
}
//初始化mysql
svc.initMysql();
//初始化redis
svc.initRedis();
return &svc;
}
// 初始化mysql
func (svc *ServiceContext) initMysql(){
// 构造mysql
svc.Mysql = make(map[string]sqlx.SqlConn);
// 初始化链接
for k, v := range svc.Config.Mysql {
dsn := fmt.Sprintf("%v:%v@tcp(%v:%v)/%v?charset=utf8mb4&parseTime=True&loc=Local",v.User,v.Pass,v.Host,v.Port,v.Dbname);
conn := sqlx.NewMysql(dsn);
// 获取数据库句柄
db,err := conn.RawDB()
if err != nil{
fmt.Println(k + " mysql链接失败")
}else{
fmt.Println(k + " mysql链接成功")
// 设置连接池信息
db.SetConnMaxIdleTime(time.Duration(v.OutTime) * time.Second) // SetConnMaxIdleTime() - 最大空闲时间
db.SetConnMaxLifetime(time.Duration(v.MaxTime) * time.Second) // SetConnMaxLifetime() - 最大可用时间
db.SetMaxIdleConns(v.MaxIdle) // SetMaxIdleConns() - 最大空闲链接数
db.SetMaxOpenConns(v.MaxOpen) // SetMaxIdleConns() - 最大链接数
}
svc.Mysql[k] = conn
}
}
// 初始化Redis
func (svc *ServiceContext) initRedis(){
// 构造Redis
svc.Redis = make(map[string]*redis.Client);
// 初始化链接
for k, v := range svc.Config.Redis {
dsn := fmt.Sprintf("%v:%v",v.Host,v.Port);
rdb := redis.NewClient(&redis.Options{
Addr: dsn,
Password: v.Pass,
DB: v.Db,
PoolSize: v.MaxOpen,
MinIdleConns: v.MaxIdle,
IdleTimeout: time.Duration(v.OutTime) * time.Second,
})
_, err := rdb.Ping(context.Background()).Result()
if err != nil{
fmt.Println(k + " redis链接失败")
}else{
fmt.Println(k + " redis链接成功")
}
svc.Redis[k] = rdb
}
}
修改逻辑层数据 - 从redis中查询用户信息
#vim internal/logic/userinfologic.go
package logic
import (
"context"
"encoding/json"
"fmt"
"time"
"afs/internal/svc"
"afs/internal/types"
"afs/internal/model"
"github.com/zeromicro/go-zero/core/logx"
)
type UserInfoLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewUserInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserInfoLogic {
return &UserInfoLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *UserInfoLogic) UserInfo(req *types.UserInfoRequest) (resp *types.UserInfoResponse, err error) {
// todo: add your logic here and delete this line
redis_key := fmt.Sprintf("afs:afs_user:id:%v",req.Uid);
value,err1 := l.svcCtx.Redis["aliyun"].Get(l.ctx, redis_key).Result()
userInfo := new(model.AfsUser);
if err1 != nil{
fmt.Println("直接读库");
info,err2 := model.NewAfsUserModel(l.svcCtx.Mysql["afs"]).GetInfoById(l.ctx,req.Uid);
if err2 == nil{
userInfo = info;
jsonByte,err3 := json.Marshal(info);
if err3 == nil{
l.svcCtx.Redis["aliyun"].Set(l.ctx, redis_key,jsonByte, time.Duration(300) * time.Second)
}
}
}else{
fmt.Println("使用缓存");
json.Unmarshal([]byte(value),userInfo)
}
resp = new(types.UserInfoResponse)
if userInfo.Id > 0{
resp.Status = 1;
resp.Message = "ok";
resp.Data = make(map[string]interface{})
resp.Data["info"] = userInfo;
}else{
resp.Status = 0;
resp.Message = "用户获取失败";
}
return
}
下载依赖包
go mod tidy
启动项目
go run afs.go