Dolphin, Go code generate Framework
Dolphin是一个Golang代码生成工具, 及ERP基础架构平台, 内部提供多种基本功能, 适合k8s中构建微服务
使用Dolphin-ui作为前端与基于Dolphin构建的后台rest交互, 部分截图
Features
- 基于XML配置生成代码
- 基于XML配置生成DOC文档
- 基于XML配置生成SQL
- 默认处理NULL空值问题
- 支持多租户分库
- 登录/退出, 或者单点登录
- 支持路由权限认证
- 快速EXCEL报表和EXCEL解析
- 支持路由缓存
- 支持数据权限控制
- 支持日子记录
- 支持RPC远程服务
- 默认生成K8s部署配置
- 支持基于数据库生成XML配置
可以前往github查看更多功能, 开源不易, 有兴趣就给个star吧 ^_^
https://github.com/2637309949/dolphin
Quick start
The first need Go installed, then you can use the below Go command to install Dolphin.
$ go get -u github.com/2637309949/dolphin/cmd/dolphin
Create project dir and run dolphin
$ mkdir example && cd example && dolphin init && dolphin build && go run main.go
Output:
time="2020/06/13 11:55:58" level=info msg="grpc listen on port:9081"
time="2020/06/13 11:55:58" level=info msg="http listen on port:8082"
Directory structure
The quasi-directory structure of the project is shown below, The project structure has been simplified as a guideline, such as managing large-scale projects and recommending new sub-projects
.
├── app
│ ├── app.auto.go
│ ├── app.go
│ ├── article.go
│ └── article.go.new
├── app.properties
├── doc
│ └── swagger.yaml
├── go.mod
├── go.sum
├── log
│ └── demo.2020071400
├── main.go
├── model
│ ├── article.auto.go
│ └── article_info.auto.go
├── rpc
│ ├── message.cli.go
│ ├── message.go
│ ├── message.go.new
│ └── proto
│ ├── message.pb.go
│ ├── message.proto
│ └── message.proto.new
├── script
│ ├── apis
│ │ ├── article.js
│ │ └── index.js
│ └── axios.js
├── sql
│ ├── article
│ │ ├── article_page_count.tpl
│ │ └── article_page_select.tpl
│ └── sqlmap
│ └── article.xml
├── srv
│ ├── article.go
│ └── worker_hello.go
├── static
│ ├── files
│ │ ├── 6b7ead55-f663-4340-a594-d282d5baf753.xlsx
│ │ └── 6dc88052-54e0-4aa9-a344-fb2b3c30f9b6.xlsx
│ └── web
│ ├── affirm.html
│ └── login.html
├── util
│ └── tool.go
└── xml
├── application.xml
├── bean
│ └── article_info.xml
├── controller
│ └── article.xml
├── rpc
│ └── message.xml
└── table
└── article.xml
CMD
Command options
dolphin, a cli tools for generate golang code
Usage:
dolphin [command]
Available Commands:
build Build project from xml
clean Removing intermediate files
help Help about any command
init Initialize a empty project
Flags:
-h, --help help for dolphin
Use "dolphin [command] --help" for more information about a command.
build
The build command generates the preset function by executing the built-in Pipeline function, You can specify that only a pipeline will be executed via the @ symbol
dolphin build @table xml/test
Existing built-in Pipeline function:
Function | Action |
---|---|
main | create main file source |
app | create engine template source |
ctr | create controller source |
proto | create proto3 source |
srv | create server source |
model | create model source |
bean | create bean source |
auto | create register source |
tool | create tool source |
sql | create sql source, .sql to .go |
sqlmap | create table sqlmap |
oauth | create oauth h5 template |
script | create js api |
deploy | create k8s template |
doc | create swagger api doc |
table | create table from datasource |
clean
The clean command clears temporary files
dolphin clean
init
The init command, as stated, generates a series of initialization files
mkdir demo && cd demo && dolphin init
API Examples
dolphin-ui
An erp template that uses dolphin background support.
You can find a number of ready-to-run examples at dolphin examples repository.
XML Label
application
application label contain app infomation, such as name, package
Example:
<?xml version="1.0" encoding="utf-8" ?>
<application name="demo" desc="template" packagename="demo"/>
application
LabelName | LabelMeaning |
---|---|
name | required, application name |
desc | application desc |
packagename | required, application packagename |
bean
bean, you can declare object in bean, just like spring bean. all bean and model will be placed in the model directory, so you needs another name if the conflict
Example:
<bean name="activity_info" desc="desc" packages="xxx" extends="$applet_activity">
<prop name="code" desc="编码" type="xx.String" />
<prop name="name" desc="名称" type="xx.String" />
</bean>
Generate code:
// Code generated by dol build. DO NOT EDIT.
package model
import (
"github.com/2637309949/dolphin/packages/null"
)
// ArticleInfo defined 文章信息
type ArticleInfo struct {
*Article
// 地址
URL null.String `json:"url" xml:"url"`
}
bean | LabelName | LabelMeaning | |----------|:-------------:| | name | bean name | | desc | bean desc | | packagename | third party package name,use "," to split | | extends | bean extends |
prop | LabelName | LabelMeaning | |----------|:-------------:| | name | prop name | | desc | prop desc | | type | prop type |
controller
controller, a collect api, you can declare api prefix
Example:
<controller name="activity" desc="微信活动" />
controller | LabelName | LabelMeaning | |----------|:-------------:| | name | controller name | | desc | controller desc | | prefix | controller desc |
api example
api, api func in controller. we has some built-in func such as 'add', 'delete', 'update', 'page', 'get', 'tree', or you can refined if you need.
add
one
<api name="add" func="add" table="sys_client" desc="添加客户端" method="post">
<param name="user" type="$sys_client" desc="客户端信息" />
<return>
<success type="$success"/>
<failure type="$fail"/>
</return>
</api>
Generate code:
// SysClientAdd api implementation
// @Summary 添加客户端
// @Tags 客户端
// @Accept application/json
// @Param Authorization header string false "认证令牌"
// @Param user body model.SysClient false "客户端信息"
// @Failure 403 {object} model.Fail
// @Success 200 {object} model.Success
// @Failure 500 {object} model.Fail
// @Router /api/sys/client/add [post]
func SysClientAdd(ctx *Context) {
var payload model.SysClient
if err := ctx.ShouldBindBodyWith(&payload, binding.JSON); err != nil {
ctx.Fail(err)
return
}
payload.ID = null.StringFromUUID()
payload.CreateTime = null.TimeFrom(time.Now().Value())
payload.Creater = null.StringFrom(ctx.GetToken().GetUserID())
payload.UpdateTime = null.TimeFrom(time.Now().Value())
payload.Updater = null.StringFrom(ctx.GetToken().GetUserID())
payload.IsDelete = null.IntFrom(0)
payload.AppName = null.StringFrom(viper.GetString("app.name"))
ret, err := ctx.PlatformDB.Insert(&payload)
if err != nil {
ctx.Fail(err)
return
}
ctx.Success(ret)
}
batch
Of course, you can also specify array parameters, which will automatically generate batch added templates.
<api name="batch_add" func="add" table="sys_role_menu" method="post" desc="添加角色菜单">
<param name="role_menu" type="[]$sys_role_menu" desc="角色菜单信息" />
<return>
<success type="$success"/>
<failure type="$fail"/>
</return>
</api>
Generate code:
// SysRoleMenuBatchAdd api implementation
// @Summary 添加角色菜单
// @Tags 角色菜单
// @Accept application/json
// @Param Authorization header string false "认证令牌"
// @Param role_menu body []model.SysRoleMenu false "角色菜单信息"
// @Failure 403 {object} model.Fail
// @Success 200 {object} model.Success
// @Failure 500 {object} model.Fail
// @Router /api/sys/role/menu/batch_add [post]
func SysRoleMenuBatchAdd(ctx *Context) {
var payload []*model.SysRoleMenu
if err := ctx.ShouldBindBodyWith(&payload, binding.JSON); err != nil {
logrus.Error(err)
ctx.Fail(err)
return
}
funk.ForEach(payload, func(form *model.SysRoleMenu) {
form.ID = null.StringFromUUID()
form.CreateTime = null.TimeFrom(time.Now().Value())
form.Creater = null.StringFrom(ctx.GetToken().GetUserID())
form.UpdateTime = null.TimeFrom(time.Now().Value())
form.Updater = null.StringFrom(ctx.GetToken().GetUserID())
form.IsDelete = null.IntFrom(0)
})
payload = funk.Filter(payload, func(form *model.SysRoleMenu) bool {
ext, _ := ctx.DB.Where("role_id=? and menu_id=?", form.RoleId.String,
form.MenuId.String).Exist(new(model.SysRoleMenu))
return !ext
}).([]*model.SysRoleMenu)
ret, err := ctx.DB.Insert(&payload)
if err != nil {
logrus.Error(err)
ctx.Fail(err)
return
}
ctx.Success(ret)
}
delete
one
The system default templates are soft delete logic, this is also highly recommended. If you need hard delete, please do it yourself
<api name="del" func="delete" table="sys_client" desc="删除客户端" method="delete">
<param name="sys_client" type="$sys_client" desc="客户端" />
<return>
<success type="$success"/>
<failure type="$fail"/>
</return>
</api>
Generate code:
// SysClientDel api implementation
// @Summary 删除客户端
// @Tags 客户端
// @Accept application/json
// @Param Authorization header string false "认证令牌"
// @Param sys_client body model.SysClient false "客户端"
// @Failure 403 {object} model.Fail
// @Success 200 {object} model.Success
// @Failure 500 {object} model.Fail
// @Router /api/sys/client/del [delete]
func SysClientDel(ctx *Context) {
var payload model.SysClient
if err := ctx.ShouldBindBodyWith(&payload, binding.JSON); err != nil {
ctx.Fail(err)
return
}
ret, err := ctx.PlatformDB.In("id", payload.ID.String).Update(&model.SysClient{
UpdateTime: null.TimeFrom(time.Now().Value()),
Updater: null.StringFrom(ctx.GetToken().GetUserID()),
IsDelete: null.IntFrom(1),
})
if err != nil {
ctx.Fail(err)
return
}
ctx.Success(ret)
}
batch
Of course, you can also specify array parameters, which will automatically generate batch deleted templates.
<api name="batch_del" func="delete" table="sys_optionset" method="delete" desc="删除字典">
<param name="user" type="[]$sys_optionset" desc="字典" />
<return>
<success type="$success"/>
<failure type="$fail"/>
</return>
</api>
Generate code:
// SysOptionsetBatchDel api implementation
// @Summary 删除字典
// @Tags 字典
// @Accept application/json
// @Param Authorization header string false "认证令牌"
// @Param user body []model.SysOptionset false "字典"
// @Failure 403 {object} model.Fail
// @Success 200 {object} model.Success
// @Failure 500 {object} model.Fail
// @Router /api/sys/optionset/batch_del [delete]
func SysOptionsetBatchDel(ctx *Context) {
var payload []*model.SysOptionset
var ids []string
if err := ctx.ShouldBindBodyWith(&payload, binding.JSON); err != nil {
logrus.Error(err)
ctx.Fail(err)
return
}
funk.ForEach(payload, func(form model.SysOptionset) {
ids = append(ids, form.ID.String)
})
ret, err := ctx.DB.In("id", ids).Update(&model.SysOptionset{
UpdateTime: null.TimeFrom(time.Now().Value()),
Updater: null.StringFrom(ctx.GetToken().GetUserID()),
IsDelete: null.IntFrom(1),
})
if err != nil {
logrus.Error(err)
ctx.Fail(err)
return
}
ctx.Success(ret)
}
update
one
The entire model field definition is null type, so don't worry about the default value types, as long as you reference packages/ xormplus (native xorm has been modified to fit null packages)
<api name="update" func="update" table="sys_client" desc="更新客户端" method="put">
<param name="user" type="$sys_role" desc="客户端信息" />
<return>
<success type="$success"/>
<failure type="$fail"/>
</return>
</api>
Generate code:
// SysClientUpdate api implementation
// @Summary 更新客户端
// @Tags 客户端
// @Accept application/json
// @Param Authorization header string false "认证令牌"
// @Param user body model.SysRole false "客户端信息"
// @Failure 403 {object} model.Fail
// @Success 200 {object} model.Success
// @Failure 500 {object} model.Fail
// @Router /api/sys/client/update [put]
func SysClientUpdate(ctx *Context) {
var payload model.SysRole
if err := ctx.ShouldBindBodyWith(&payload, binding.JSON); err != nil {
ctx.Fail(err)
return
}
payload.Updater = null.StringFrom(ctx.GetToken().GetUserID())
payload.UpdateTime = null.TimeFrom(time.Now().Value())
ret, err := ctx.PlatformDB.ID(payload.ID).Update(&payload)
if err != nil {
ctx.Fail(err)
return
}
ctx.Success(ret)
}
batch
Of course, you can also specify array parameters, which will automatically generate batch updated templates.
<api name="batch_update" func="update" table="article" desc="更新文章" method="put">
<param name="article" type="[]$article" desc="文章信息" />
<return>
<success type="$success"/>
<failure type="$fail"/>
</return>
</api>
Generate code:
// ArticleBatchUpdate api implementation
// @Summary 更新文章
// @Tags 文章
// @Accept application/json
// @Param Authorization header string false "认证令牌"
// @Param article body []model.Article false "文章信息"
// @Failure 403 {object} model.Fail
// @Success 200 {object} model.Success
// @Failure 500 {object} model.Fail
// @Router /api/article/batch_update [put]
func ArticleBatchUpdate(ctx *Context) {
var payload []*model.Article
var err error
var ret []int64
var r int64
if err = ctx.ShouldBindBodyWith(&payload, binding.JSON); err != nil {
logrus.Error(err)
ctx.Fail(err)
return
}
s := ctx.DB.NewSession()
s.Begin()
defer s.Close()
funk.ForEach(payload, func(form model.Article) {
form.Updater = null.StringFrom(ctx.GetToken().GetUserID())
form.UpdateTime = null.TimeFrom(time.Now().Value())
r, err = s.ID(form.ID.String).Update(&form)
ret = append(ret, r)
})
if err != nil {
s.Rollback()
logrus.Error(err)
ctx.Fail(err)
return
}
ctx.Success(ret)
}
page
The most basic paging interface template is also supported by default.
<api name="page" func="page" table="sys_client" desc="客户端分页查询" method="get">
<param name="page" type="int" value="1" desc="页码"/>
<param name="size" type="int" value="10" desc="单页数"/>
<param name="app_name" type="string" desc="所属应用"/>
<return>
<success type="$success"/>
<failure type="$fail"/>
</return>
</api>
Generate code:
// SysClientPage api implementation
// @Summary 客户端分页查询
// @Tags 客户端
// @Param Authorization header string false "认证令牌"
// @Param page query int false "页码"
// @Param size query int false "单页数"
// @Failure 403 {object} model.Fail
// @Success 200 {object} model.Success
// @Failure 500 {object} model.Fail
// @Router /api/sys/client/page [get]
func SysClientPage(ctx *Context) {
q := ctx.TypeQuery()
q.SetInt("page", 1)
q.SetInt("size", 10)
q.SetString("app_name", viper.GetString("app.name"))
q.SetTags()
ret, err := ctx.PageSearch(ctx.PlatformDB, "sys_client", "page", "sys_client", q.Value())
if err != nil {
ctx.Fail(err)
return
}
ctx.Success(ret)
}
tree
Tree structure is also a common algorithm. The framework integrates a TreeSearch interface for tree retrieval.
<api name="page" func="page" table="sys_menu" desc="菜单分页查询" method="get">
<param name="page" type="int" value="1" desc="页码"/>
<param name="size" type="int" value="10" desc="单页数"/>
<return>
<success type="$success"/>
<failure type="$fail"/>
</return>
</api>
Generate code:
// SysMenuTree api implementation
// @Summary 菜单树形结构
// @Tags 菜单
// @Param Authorization header string false "认证令牌"
// @Failure 403 {object} model.Fail
// @Router /api/sys/menu/tree [get]
func SysMenuTree(ctx *Context) {
q := ctx.TypeQuery()
q.SetString("name")
q.SetRule("sys_menu_tree")
q.SetTags()
ret, err := ctx.TreeSearch(ctx.DB, "sys_menu", "tree", "sys_menu", q.Value())
if err != nil {
ctx.Fail(err)
return
}
ctx.Success(ret)
}
TreeSearch:
Detailed instructions can be found in the TreeSearch source code
// platform/app/app.ctx.go#TreeSearch
func (ctx *Context) TreeSearch(db *xorm.Engine, controller, api, table string,
q map[string]interface{}) (interface{}, error)
one
Find a single piece of data by unique key.
<api name="get" func="one" table="sys_client" desc="获取客户端信息" method="get">
<param name="id" type="string" desc="客户端id" />
<return>
<success type="$success"/>
<failure type="$fail"/>
</return>
</api>
Generate code:
// SysClientGet api implementation
// @Summary 获取客户端信息
// @Tags 客户端
// @Param Authorization header string false "认证令牌"
// @Param id query string false "客户端id"
// @Failure 403 {object} model.Fail
// @Success 200 {object} model.Success
// @Failure 500 {object} model.Fail
// @Router /api/sys/client/get [get]
func SysClientGet(ctx *Context) {
var entity model.SysClient
id := ctx.Query("id")
_, err := ctx.PlatformDB.ID(id).Get(&entity)
if err != nil {
ctx.Fail(err)
return
}
ctx.Success(entity)
}
other
In addition to the default interface, custom interfaces are also supported.
<api name="payment" method="post" desc="文章付费">
<param name="article" type="$article_info" desc="文章"/>
<return>
<success type="$success"/>
<failure type="$fail"/>
</return>
</api>
Generate code:
// ArticlePayment api implementation
// @Summary 文章分页查询
// @Tags 文章
// @Accept application/json
// @Param Authorization header string false "认证令牌"
// @Param article body model.ArticleInfo false "文章"
// @Failure 403 {object} model.Fail
// @Success 200 {object} model.Success
// @Failure 500 {object} model.Fail
// @Router /api/article/payment [post]
func ArticlePayment(ctx *Context) {
var payload model.ArticleInfo
if err := ctx.ShouldBindBodyWith(&payload, binding.JSON); err != nil {
ctx.Fail(err)
return
}
ret, err := srv.ArticleAction(payload)
if err != nil {
ctx.Fail(err)
return
}
ctx.Success(ret)
}
可以前往github查看更多功能, 有兴趣就给个star吧 ^_^