简介
基于 beego 封装的脚手架,纯后端项目,封装了一些常用的项目基本功能,项目结构如下:
- conf:配置读取;
- controllers:基础 api 方法封装,应用 api;
- iorm:beego 的持久化模块扩展,提供了实体泛型,封装了通用的 crud 方法,事务方法,增加了软删除机制;
- logger:日志模块封装,日志文件分割配置;
- models:基础实体定义;
- routers:api 路由注册,路由常用配置;
- main:应用启动入口。
模块
controllers
基于 web.Controller 扩展,提供通用方法如入参 body 校验,请求逻辑终止,错误码处理,统一响应结构。
controller 示例:
type ExampleController struct {
BaseController
}
func (ctrl ExampleController) Success() {
ctrl.writeSuccess("success")
}
func (ctrl ExampleController) Authentication() {
ctrl.abort401()
}
func (ctrl ExampleController) InternalServerError() {
ctrl.abort("internal server error")
}
func (ctrl ExampleController) Body() {
type param struct {
String string `json:"string" valid:"Required"`
Int int `json:"int" valid:"Range(1, 10)"`
Bool bool `json:"bool"`
Name string `json:"name" valid:"MaxSize(5)"`
}
body := param{}
err := ctrl.BindJSON(&body)
if err != nil {
ctrl.abort(err.Error())
}
ok, errors := ctrl.validWithoutAbort(&body)
if !ok {
fmt.Println(errors)
}
ctrl.valid(&body)
ctrl.writeSuccess(body)
}
错误码处理:
// 注册 controller 错误自定义处理
web.ErrorController(&controllers.ErrorController{})
const errorHandlerMsgKey = "error"
type ErrorController struct {
web.Controller
}
func (ctrl *ErrorController) Error400() {
ctrl.error(400, "bad request")
}
func (ctrl *ErrorController) Error401() {
ctrl.error(401, "unauthorized")
}
func (ctrl *ErrorController) Error403() {
ctrl.error(403, "forbidden")
}
func (ctrl *ErrorController) Error404() {
ctrl.error(404, "page not found")
}
func (ctrl *ErrorController) Error500() {
ctrl.error(500, "internal server error")
}
func (ctrl *ErrorController) error(code int, err string) {
if errorString, ok := ctrl.Data[errorHandlerMsgKey].(string); ok {
err = errorString
}
_ = ctrl.Ctx.JSONResp(models.ErrorResponseWithCode(code, err))
}
iorm
beego 的持久化模块扩展,提供了基础实体类型定义,封装了通用的 crud 方法,事务方法,增加 deleted_at 字段实现了软删除操作。
基础实体类型定义:
type IModel interface {
TableName() string
GetId() int64
SetId(id int64)
SetDeleteAt(time time.Time)
BeforeInsert()
}
type Model struct {
Id int64 `orm:"pk;column(id);description(primary key)"`
CreatedAt time.Time `orm:"auto_now_add;type(datetime)"`
UpdatedAt time.Time `orm:"auto_now;type(datetime)"`
DeletedAt time.Time `orm:"null"`
CreatedBy string `orm:"size(50);null"`
Creator string `orm:"size(50);null"`
UpdateBy string `orm:"size(50);null"`
Updater string `orm:"size(50);null"`
}
func (m *Model) GetId() int64 {
return m.Id
}
func (m *Model) SetId(id int64) {
m.Id = id
}
func (m *Model) SetDeleteAt(time time.Time) {
m.DeletedAt = time
}
func (m *Model) BeforeInsert() {
m.Id = int64(utils.GenSnowFlakeId())
if m.CreatedBy == "" {
m.CreatedBy = "system"
}
if m.Creator == "" {
m.Creator = "system"
}
if m.UpdateBy == "" {
m.UpdateBy = "system"
}
if m.Updater == "" {
m.Updater = "system"
}
}
iorm,使用Transaction方法开启事务,并使用 T 开头的事务方法;默认查询方法只会查询deleted_at字段不为空的记录,删除则为软删除,调用UnScoped()方法则代表进入真实查询与真实删除模式,代码主要基于orm.QuerySeter实现。
代码示例:
type iModel = models.IModel
var unScopedCondition *orm.Condition
func init() {
unScopedCondition = orm.NewCondition().And("deleted_at__isnull", true)
}
type ormConfig struct {
desc bool
condition *orm.Condition
unScoped bool
}
type QueryOption func(config *ormConfig)
func Desc() QueryOption {
return func(config *ormConfig) {
config.desc = true
}
}
func WithCondition(condition *orm.Condition) QueryOption {
return func(config *ormConfig) {
config.condition = condition
}
}
type IOrm[M any] struct {
unScoped bool
}
func (iorm *IOrm[M]) querySeterWithConfig(qs orm.QuerySeter, config *ormConfig) orm.QuerySeter {
if config == nil {
return qs
}
if config.desc {
qs = qs.OrderBy("-created_at")
}
if config.condition != nil {
qs = qs.SetCond(config.condition)
}
if !config.unScoped {
if qs.GetCond() == nil {
qs = qs.SetCond(unScopedCondition)
} else {
qs = qs.SetCond(qs.GetCond().AndCond(unScopedCondition))
}
}
return qs
}
func (iorm *IOrm[M]) Find(m iModel, options ...QueryOption) ([]*M, error) {
var mds []*M
qs := orm.NewOrm().QueryTable(m.TableName())
config := iorm.handleOptions(options...)
qs = iorm.querySeterWithConfig(qs, config)
_, err := qs.All(&mds)
return mds, err
}
func (iorm *IOrm[M]) Create(m iModel) error {
o := orm.NewOrm()
return iorm.TCreate(o, m)
}
func (iorm *IOrm[M]) TCreate(tx orm.QueryExecutor, m iModel) error {
m.BeforeInsert()
// Insert 方法返回主键自增的值,需要设置主键自增
_, err := tx.Insert(m)
return err
}
func (iorm *IOrm[M]) UnScoped() *IOrm[M] {
return &IOrm[M]{unScoped: true}
}
func (iorm *IOrm[M]) Transaction(fc func(ctx context.Context, tx orm.TxOrmer) error) {
_ = orm.NewOrm().DoTx(fc)
}
定义示例:
type Example struct {
models.Model
String string `orm:"null"`
Text string `orm:"type(text);null"`
Bool bool
Parent int64
}
func (Example) TableName() string {
return "example"
}
type ExampleOrm struct {
IOrm[Example]
}
var exampleOrm = &ExampleOrm{}
var example = Example{
String: "hello world",
Text: "text",
Bool: true,
}
查询调用示例:
func TestFind(t *testing.T) {
condition := orm.NewCondition().And("id__in", []int64{1})
// 查询
exampleOrm.Find(&Example{})
// 倒序条件
exampleOrm.Find(&Example{}, Desc())
// 添加自定义条件
exampleOrm.Find(&Example{}, Desc(), WithCondition(condition))
// 真实查询
exampleOrm.UnScoped().Find(&Example{}, Desc(), WithCondition(condition))
// 查询
exampleOrm.FindWithPaging(&Example{}, 1, 10)
// 倒序条件
exampleOrm.FindWithPaging(&Example{}, 1, 10, Desc())
// 添加自定义条件
exampleOrm.FindWithPaging(&Example{}, 1, 10, Desc(), WithCondition(condition))
// 真实查询
exampleOrm.UnScoped().FindWithPaging(&Example{}, 1, 10, Desc(), WithCondition(condition))
}
创建调用示例:
func TestCreate(t *testing.T) {
// 添加
exampleOrm.Create(example)
// 事务
exampleOrm.Transaction(func(tx *gorm.DB) error {
exampleOrm.TCreate(tx, example)
return errors.New("transaction err")
})
}
最后
完整代码已经上传仓库:igolang,更多示例请参考模块目录下的 test 文件。