towelong / lin-cms-go

lin-cms的go实现

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool


Lin-CMS-Go

Lin-CMS的go语言实现, 简要介绍下LinCMS: Lin CMS 是一个前后端分离的 CMS 解决方案。其他信息请移步官方文档

LinCMS文档资料

文档地址

线上 Demo

案例


Lin-CMS-Go

安装依赖

Go >= 1.16

$ go mod tidy

目录结构

.
├── Makefile
├── README.md
├── api
│   └── app  ---- 存放API文件
│       ├── cms
│       │   ├── admin.go
│       │   ├── file.go
│       │   ├── user.go
│       │   └── user_test.go
│       ├── v1
│       │   └── book.go
│       └── wire_set.go   --- 提供对接口的依赖注入
├── cmd
│   └── app
│       ├── main.go -- 服务启动的入口文件
│       ├── pprof.go  -- go的pprof服务
│       ├── server.go -- 服务启动所需的依赖
│       ├── wire.go -- 注入器
│       ├── wire_gen.go
│       └── wire_set.go -- 为注入器提供依赖
├── config ----配置文件
│   ├── config.dev.yaml
│   ├── config.prod.yaml
│   └── config.yaml
├── go.mod
├── go.sum
├── internal
│   ├── config -- 解析配置文件
│   │   ├── config.go
│   │   └── config_test.go
│   ├── domain  -- 业务模型等等
│   │   ├── dto
│   │   │   ├── group.go
│   │   │   ├── permission.go
│   │   │   └── user.go
│   │   ├── model
│   │   │   ├── base_model.go
│   │   │   ├── book.go
│   │   │   ├── file.go
│   │   │   ├── group.go
│   │   │   ├── group_permission.go
│   │   │   ├── log.go
│   │   │   ├── permission.go
│   │   │   ├── user.go
│   │   │   ├── user_group.go
│   │   │   └── user_identity.go
│   │   └── vo
│   │       ├── group.go
│   │       ├── page.go
│   │       ├── permission.go
│   │       └── user.go
│   ├── injector.go
│   ├── middleware  ---中间件
│   │   ├── auth.go
│   │   ├── cors.go
│   │   └── error.go
│   ├── pkg
│   │   ├── db
│   │   │   └── gorm.go
│   │   └── jwt
│   │       ├── jwt_maker.go
│   │       └── wire_set.go
│   ├── router --- 路由注册
│   │   └── router.go
│   ├── service --- 接口服务
│   │   ├── constants.go
│   │   ├── file.go
│   │   ├── group.go
│   │   ├── mock
│   │   │   ├── group.go
│   │   │   └── user.go
│   │   ├── permission.go
│   │   ├── user.go
│   │   └── wire_set.go
│   └── web.go
└── pkg --- 一些常用的包
    ├── code.go --- 业务状态码
    ├── crypt.go --- 加密解密模块
    ├── crypt_test.go
    ├── response  --- 通用的相应封装
    │   ├── common.go
    │   └── response.go
    ├── router
    │   └── lin_router.go
    ├── token
    │   ├── constants.go
    │   ├── jwt.go
    │   ├── jwt_test.go
    │   └── payload.go
    └── validator -- 参数校验
        └── validator.go

如何使用?

使用

对于本程序来说有一点的学习成本,由于在这个项目中用到了 Google的 wire来实现依赖的注入,因此,大家需要对wire有一定的了解. 不懂也没有关系,通过本文档进行简单的了解之后,也可以帮助大家进行使用。

开发API的基本步骤

v1/book/login这个接口为例。 首先API放置在api/app 这个目录下,cms目录对应的是cms相关的API, v1目录放置自身的业务API。

  1. 声明一个结构体
type BookAPI struct {
	
}
  1. 为这个结构体声明一个方法
func (book *BookAPI) GetBookList(ctx *gin.Context) {
	ctx.JSON(200, gin.H{
		"book": "list",
	})
}
  1. 最终声明一个注册路由的方法
func (book *BookAPI) RegisterServer(routerGroup *gin.RouterGroup) {
	bookRouter := router.NewLinRouter("/book", "图书", routerGroup)
	bookRouter.GET("/list", book.GetBookList)
	bookRouter.LinDELETE("DeleteBook",
		"/",
		bookRouter.Permission("删除图书", true),
		book.Auth.GroupRequired,
		book.DeleteBook,
	)
}
  1. 为图书的API注册到主路由上 私有的一些模块放置到了internal目录里面,而router也在其中。

internal/router/router.go

type Router struct {
	BookAPI  *v1.BookAPI
  .....
}

在RegisterAPI方法中, 声明一个v1的路由组

  v1Group := app.Group("/v1")
	{
		r.BookAPI.RegisterServer(v1Group)
	}

到此为止,一个简单的API就写完了。

进阶使用

在刚才的基础教程中,并没有实际的业务逻辑,那么在这里通过实际的业务逻辑来进行实战操作。 根据刚才的目录结构可以知道,开发一个业务需要在service中进行。

本次以 cms/user/login API 为例

service/user.go

type IUserService interface {
	VerifyUser(username, password string) (model.User, error)
}

type UserService struct {
	DB           *gorm.DB
}

func (u UserService) VerifyUser(username, password string) (model.User, error) {
	var (
		userIdentity model.UserIdentity
		user         model.User
	)
	db := u.DB.Where("identity_type = ? AND identifier = ?", UserPassword.String(), username).First(&userIdentity)
	if db.Error != nil {
		return user, response.NewResponse(10031)
	}
	verifyPsw := pkg.VerifyPsw(password, userIdentity.Credential)
	if verifyPsw {
		err := u.DB.Where("username = ?", username).First(&user).Error
		if err != nil {
			return user, response.NewResponse(10031)
		}
		return user, nil
	}
	return user, response.NewResponse(10032)
}

可以看到定义了一个接口IUserService,以及一个结构体UserService,结构体中有DB,那么这个DB(gorm)就是拿来操作数据库的。

最后定义了一个结构体方法VerifyUser,里面就书写了一系列的业务逻辑。到这里并没有结束!

那么大家可能会好奇,这个DB并没有通过什么方法进行传递赋值,为什么可以直接拿来用呢? 这个就是wire这个库在起作用,他是一个依赖注入的库,通过自动生成来处理我们模块与模块之间的依赖。

写到这里大家就差不多能理解了。

但是wire并没有SpringBoot那么隐式的帮我们注入依赖,而是通过了一个wire_set来帮我们管理依赖。在service目录下有个wire_set.go文件,里面放的就是需要被wire管理的依赖。

var Set = wire.NewSet(
	wire.Struct(new(UserService), "*"),
	wire.Bind(new(IUserService), new(*UserService)),
)

这样我们就完成了整个service的开发。

回到控制器 api/app/cms/user.go

type UserAPI struct {
	JWT         token.IToken
	UserService service.IUserService
}

在这里面直接注入IUserService即可。

最后路由是这样的

func (u UserAPI) RegisterServer(routerGroup *gin.RouterGroup) {
	userRouter := router.NewLinRouter("/user", "用户", routerGroup)
	userRouter.POST("/login", u.Login)
}

除此之外这个UserAPI也需要被wire托管。 同样的在api/app/wire_set.go中,进行注册即可。

var APISet = wire.NewSet(
	wire.Struct(new(cms.UserAPI), "*"),
)

最重要的一点:wire_set要生效,必须 使用 go generate ./... 来进行生成wire帮助我们处理依赖的文件 cmd/app/wire_gen.go

但其实看起来跟我们手动去管理依赖的方式是一样的。它看起来像这样:

// Injectors from wire.go:

func NewInjector() (*internal.Injector, error) {
	gormDB, err := db.InitDB()
	if err != nil {
		return nil, err
	}
	userService := &service.UserService{
		DB:           gormDB,
		GroupService: groupService,
	}
	routerRouter := &router.Router{
		AdminAPI: adminAPI,
		UserAPI:  userAPI,
		BookAPI:  bookAPI,
	}
  ....  // 此处进行省略
	return injector, nil
}

LinRouter

路由的使用:使用过koa的小伙伴应该知道,go的gin框架也是属于类koa的一个库,在gin自带的路由基础上进行了进一步的封装。

用起来像这样:

 Auth的引入都是通过注入来进行引入,直接放在结构体中就可以。
type UserAPI struct {
	UserService service.IUserService
	Auth        middleware.Auth
}

// 权限路由 参考 api/app/cms/admin.go
adminRouter := router.NewLinRouter("/admin", "管理员", routerGroup)
	adminRouter.LinGET(
		"GetAllPermissions",
		"/permission",
		adminRouter.Permission("查询所有可分配的权限", false),
		admin.Auth.AdminRequired,
		admin.GetAllPermissions,
	)
// 不需要挂载权限的路由
userRouter := router.NewLinRouter("/user", "用户", routerGroup)
userRouter.POST("/register", u.Auth.AdminRequired, u.Register)
// 普通路由
bookRouter := router.NewLinRouter("/book", "图书", routerGroup)
	bookRouter.GET("/list", book.GetBookList)

License

MIT

About

lin-cms的go实现


Languages

Language:Go 92.2%Language:PLpgSQL 7.4%Language:Makefile 0.4%