热更新运行
gf run main.go
创建项目模板
gf init demo -u
// 项目名称demo -u使用最新版本
自动生成dao
文件
gf gen dao
设计表结构,初始化项目,修改配置文件
使用 gf gen dao
生成对应的 dao/do/model
编写api
层:定义数据侧数据结构,提供对内的数据处理的输入/输出结构
编写model
层:定义数据侧数据结构,提供对内的数据处理的输入/输出数据结构
编写logic
层,自动生成service
层代码。(通过配置goland File Watcher
自动生成,也可以通过gf gen service
手动执行脚本生成,建议前者)
在service
层代码生成RegisterXX()
方法后,在对应的logic
模块注册服务(每个模块只需要写一次)
编写controller
层,接收/解析用户输入的参数,调用service
层的服务。
注册路由,对外暴露接口,比如这个项目是编写cmd.go
文件。
在main.go
中 加入一行 _ "project-name/internal/logic"
(只需写一次)
在main.go
中加入一行 _ "github.com/gogf/gf/contrib/drivers/mysql/v2"
(如果你使用的是mysql
;只需写一次)
在main.go
中加入一行_ "github.com/gogf/gf/contrib/nosql/redis/v2"
(如果使用redis`;只需写一次)
其中9、10只需要添加一次; 步骤6每个模块只需要写一次
gdb
模块支持对时间写入、更新、删除的自动填充,若要使用该特性,我们约定:
null
date,datetime,timestamp
created_at
用于记录创建时更新,仅会写入一次updated_at
用于记录修改时更新,每次记录变更时更新deleted_at
用于记录的软删除特性,只有当记录删除时会写入一次字段名称其实不区分大小写,也会忽略特殊字符,例如CreatedAt,UpdatedAt,DeletedAt
也是支持的。此外,时间字段名称可以通过配置文件进行自定义修改,并可使用TimeMaintainDisabled
配置完整关闭该特性。
Unscoped
用于在链式操作中忽略自动时间更新特性,例如上面的示例,加上Unscoped
方法后:
//SELECT*FROM`user`WHEREuid>1
g.Model("user").Unscoped().Where("uid>?",1).All()
//SELECT*FROM`user`AS`u`LEFTJOIN`user_detail`AS`ud`ON(ud.uid=u.uid)WHEREu.uid=10LIMIT1
g.Model("user","u").LeftJoin("user_detail","ud","ud.uid=u.uid").Where("u.uid",10).Unscoped().One()
当数据表中存在deleted_at
字段时,所有涉及到该表的查询操作都将自动加上deleted_at IS NULL
的条件
可使用unscoped
实现硬删除
dao.RotationInfo.Ctx(ctx).Where(g.Map{
dao.RotationInfo.Columns().Id: id,
}).Unscoped().Delete()
Fields
用于指定需要操作的表字段,包括查询字段、写入字段、更新字段等过滤;FieldsEx
用于例外的字段指定,可用于查询字段、写入字段、更新字段等过滤;// SELECT `uid`,`nickname` FROM `user` ORDER BY `uid` asc
g.Model("user").Fields("uid, nickname").Order("uid asc").All()
当 map
/struct
中存在空值如 nil
,""
,0
时,默认情况下,gdb
将会将其当做正常的输入参数,因此这些参数也会被更新到数据表。OmitEmpty
特性可以在将数据写入到数据库之前过滤空值数据的字段。
func (m *Model) OmitEmpty() *Model
func (m *Model) OmitEmptyWhere() *Model
func (m *Model) OmitEmptyData() *Model
OmitEmpty
方法会同时过滤Where
及Data
中的空值数据,而通过OmitEmptyWhere/OmitEmptyData
方法可以执行特定的字段过滤。
批量写入/更新操作中OmitEmpty
方法将会失效,因为在批量操作中,必须保证每个写入记录的字段是统一的。
加盐加密是一种对系统登录口令的加密方式,它实现的方式是将每一个口令跟一个n位随机数相关联,这个n位随机数叫做”盐“(salt)。
无论何时只要口令改变,随机数就改变。随机数以未加密的方式存放在口令文件中,这样每个人都可以读。不再只保存加密过的口令,而是先将口令和随机数连接起来然后一同加密,加密后的结果放在口令文件中。
定义表结构技巧
整体业务逻辑编写
上传文件的注意问题
*ghttp.UploadFile
的使用、MustGet
的使用技巧
*ghttp.UploadFile
:上传文件类型MustGet
:MustGet
方法与Get
类似,也是配置对象中获取配置数据,组装成gvar
结构,但是返回参数只有一个:*gvar.Var
链式操作WhereGTE
的使用
ORM中用于查询条件的gte、gt、lte、lt
分别为大于等于、大于、小于等于、小于
抽取常量文件
gerror.NewCode()
自定义错误码和错误信息
gerror.NewCode(gcode.CodeMissingParameter, consts.CodeMissingParameterMsg)
gerror.New(consts.CodeMissingParameterMsg)
goframe支持swagger:http://127.0.0.1:8000/swagger/和api的json格式文件: http://127.0.0.1:8000/api.json
在apipost或postman中可以直接使用url导入,也可下载文件后导入
该方法可以实现对任意参数到struct/struct数组/map/map数组
的转换,并且根据开发者输入的转换目标参数自动识别执行转换。
func main() {
type User struct {
Uid int
Name string
}
params := g.Map{
"uid": 1,
"name": "john",
}
var user *User
if err := gconv.Scan(params, &user); err != nil {
panic(err)
}
g.Dump(user)
}
输出:
{
Uid: 1,
Name: "john",
}
min-length
min-length:min
min
(长度参数为整形),注意底层使用Unicode
计算长度,因此中文一个汉字占1
个长度单位。min
min:min
min
(支持整形和浮点类型参数)。可以使用entity.XXInfo
来复用结构体内的元素
登录成功后保存id:
r.SetCtxVar(consts.CtxAdminId, adminInfo.Id) //r *ghttp.Request
需要使用时取出:
data.UserId = gconv.Int(ctx.Value(consts.CtxAdminId)) //ctx context.Context
model层:
type CollectionListOutputItem struct {
Id int `json:"id" description:""`
UserId int `json:"user_id" description:"用户id"`
ObjectId int `json:"object_id" description:"对象id"`
Type int `json:"type" description:"收藏类型:1商品 2文章"`
Goods GoodsItem `json:"goods" orm:"with:id=object_id"`
Article ArticleItem `json:"article" orm:"with:id=object_id"` // 将Article的主键id与ObjectId关联
}
type GoodsItem struct {
g.Meta `orm:"table:goods_info"`
Id uint `json:"id"`
}
type ArticleItem struct {
g.Meta `orm:"table:article_info"`
Id uint `json:"id"`
}
logic层:
listModel := dao.CollectionInfo.Ctx(ctx).Page(in.Page, in.Size).
Where(dao.CollectionInfo.Columns().Type, in.Type)
if in.Type == consts.CollectionTypeGoods {
err = listModel.With(model.GoodsItem{}).Scan(&out.List)
} else if in.Type == consts.CollectionTypeArticle {
err = listModel.With(model.ArticleItem{}).Scan(&out.List)
} else {
err = listModel.WithAll().Scan(&out.List)
}
为方便安全执行事务操作,ORM
组件同样提供了事务的闭包操作,通过Transaction
方法实现,该方法定义如下:
func (db DB) Transaction(ctx context.Context, f func(ctx context.Context, tx TX) error) (err error)
当给定的闭包方法返回的error
为nil
时,那么闭包执行结束后当前事务自动执行Commit
提交操作;否则自动执行Rollback
回滚操作。闭包中的context.Context
参数为goframe v1.16
版本后新增的上下文变量,主要用于链路跟踪传递以及嵌套事务管理。由于上下文变量是嵌套事务管理的重要参数,因此上下文变量通过显示的参数传递定义。
如果闭包内部操作产生
panic
中断,该事务也将自动进行回滚,以保证操作安全。
# github.com/elastic/go-sysinfo/providers/windows
C:\Users\ilotus\go\pkg\mod\github.com\elastic\[email protected]\providers\windows\process_windows.go:307:14: assignment mismatch: 2 variables but tokenUser.User.Sid.String returns 1 value
C:\Users\ilotus\go\pkg\mod\github.com\elastic\[email protected]\providers\windows\process_windows.go:316:15: assignment mismatch: 2 variables but tokenGroup.PrimaryGroup.String returns 1 value
经过检查发现是版本错误,访问Releases · elastic/go-sysinfo (github.com)在Releases最新版为1.14.2,在go.mod中修改版本
github.com/elastic/go-sysinfo v1.14.2
然后使用go mod tidy
下载新版本
userInfo.SecretAnswer == in.SecretAnswer
userInfo.SecretAnswer
类型为interface{}
in.SecretAnswer
类型为string
不同类型不能做比较
修改为:
gconv.String(userInfo.SecretAnswer) == in.SecretAnswer