Go语言简洁框架目录和高效的快发框架设计

前言

一个语言是否好友除了语言语法及内置包以外,还需要有一个设计不错框架,我们认为好的框架目录是一定是简洁的,目录结构都不能合理设计,那我们也不敢相信他能把框架设计的好。一个简洁的框架是可以让框架易学,让新人的快速上手。不论您公司人员流动如何,一定有新人加入,当一个新人拿到框架,如果能让他快速上手,肯定是能给您公司减少成本的。说了这么希望我们能达成框架目录结构的简洁理念,接下来我们一起看看我们设计简洁目录的框架咯。

框架目录

先看看框架目录:

├── app                      # 应用目录
│   ├── admin                # 后台管理应用模块(安装saas时存在、不安装则删除)
│   ├── business             # 业务端应用模块
│   ├── common               # 公共应用模块
│   └── controller.go        # 应用控制器
├── devsource                # 开发静态资源(安装界面、代码生成模板)
├── resource                 # 静态资源及配置文件(发布应用带上)
├── utils                    # 框架核心代码及工具包
├── go.mod                   # 依赖包管理工具
├── go.sum         
├── main.go                  # main函数       
├── runner.conf              # fresh热编译配置文件
└── README.md                # 项目介绍

从目录总我们看到这个目录很简洁,这里的简洁不是为了简单而简单,而是在实用基础上进行精简。

框架设计中我们把框架的核心代码放在utils中,在项目开发中在非必要情况下不要去改动utils目录下的代码,业务代码放在app目录下,静态资源分别放在devsource和resource中(为什么分两个目录呢,这是因为方便部署直接拷贝resource目录带部署环境中,devsource是开始时用到资源到在生产环境中用不到,所以分开可以减少部署时上传资源)。目录结构中我们只要3个类型目录:一个框架核心代码目录、一个开发业务目录、一个静态资源目录。大道至简,开发时技术员只需在app目录下编写代码即可,不会把框架代码和业务代码混合在一起。

接下来在看看app业务开发目录,这是开发时核心目录,也需要简洁。首先app目录下有个controller.go控制器文件,他是用来引入同目录文件目录,这是框架设计自动生成请求路径用到的。也就是说这个框架不需开发者开发一个接口手动添加一个请求路由,框架会会根据目录层级生成路由。

应用控制器

app下面的应该控制器代码如下:

Go 复制代码
package controller

/**
* app路由引入口《引入模块控制器》
*/
import (
	"gofly/app/admin"
	"gofly/app/business"
	_ "gofly/app/common"
	"gofly/utils/gf"
)

// 路由中间件/路由钩子
func RouterHandler(c *gf.GinCtx) {
	business.RouterHandler(c, "business")
	admin.RouterHandler(c, "admin")
}

再看app下的admin和business 这两个是框架的两个后台的后端,如果开发时业务不需有多租户我们就删除admin,这样app目录下就只剩下business和common了。所以说这个框架很简洁,也容易扩展多个端再加个目录即可。

模块目录

在继续看模块(即app下面目录)目录下的目录结构,这里就直接用business模块举例子了,选是system展开说明:

Go 复制代码
├── developer          # 开发者工具
├── datacenter         # 数据中心-数据字典-配置
├── user               # 用户登录等操作
├── system             # 后台系统管理(展开举例)
│   ├── account.go     # 用户管理
│   ├── dept.go        # 部门管理
│   ├── log.go         # 系统日志
│   ├── role.go        # 角色管理
│   └── rule.go        # 系统菜单
│
├── createcode         # 代码生成示例代码 
│   ├── product.go     # 演示产品
│   └── productcate.go # 演示产品分类
│
└── controller.go      # 模块控制器-也就是business这个模块的控制   

模块控制器

模块下的控制器代码如下:

Go 复制代码
package business

/**
* 引入控制器-文件夹名称的路径
*/
import (
	_ "gofly/app/business/datacenter"
	_ "gofly/app/business/developer"
	_ "gofly/app/business/system"
	_ "gofly/app/business/user"
	"gofly/utils/gf"
)

// 路由中间件/路由钩子,noAuths无需路由验证接口,可以从c获取请求各种参数
func RouterHandler(c *gf.GinCtx, modelname string) {
	if gf.IsModelPath(c.FullPath(), modelname) { //在这里面处理拦截操作,如果需要拦截终止执行则:c.Abort()
		// 判断请求接口是否需要验证权限(RBAC的权限)
		if gf.NeedAuthMatch(c) {
			haseauth := gf.CheckAuth(c, modelname)
			if haseauth {
				c.Next()
			} else {
				gf.Failed().SetMsg(gf.LocaleMsg().SetLanguage(c.Request.Header.Get("locale")).Message("sys_auth_permission")).SetData(haseauth).Regin(c)
				c.Abort()
			}
		} else {
			c.Next()
		}
	}
}

接口代码实现

在看看如下编写接口代码,我们用createcode这个代码示例类举例,首先创建一个product.go,然后创建增删改查的接口,示例代码如下:

Go 复制代码
package createcode

import (
	"gofly/utils/gf"
	"gofly/utils/tools/gmap"
)

// 演示代码-产品管理
type Product struct{}

func init() {
	fpath := Product{}
	gf.Register(&fpath, fpath)
}

// 获取列表
func (api *Product) GetList(c *gf.GinCtx) {
	pageNo := gf.Int(c.DefaultQuery("page", "1"))
	pageSize := gf.Int(c.DefaultQuery("pageSize", "10"))
	//搜索添条件
	param, _ := gf.RequestParam(c)
	whereMap := gmap.New()
	whereMap.Set("business_id", c.GetInt64("businessID"))
	if cid, ok := param["cid"]; ok && gf.Int(cid) != 0 {
		cids := gf.CateAllChilId("createcode_product_cate", cid)
		whereMap.Set("cid In(?)", cids)
	}
	if title, ok := param["title"]; ok && title != "" {
		whereMap.Set("title like ?", "%"+gf.String(title)+"%")
	}
	if status, ok := param["status"]; ok && status != "" {
		whereMap.Set("status", status)
	}
	if createtime, ok := param["createtime"]; ok && createtime != "" {
		datetime_arr := gf.SplitAndStr(gf.String(createtime), ",")
		whereMap.Set("createtime between ? and ?", gf.Slice{datetime_arr[0] + " 00:00", datetime_arr[1] + " 23:59"})
	}

	if userType, ok := param["userType"]; ok && userType != "" {
		whereMap.Set("userType", userType)
	}
	MDB := gf.Model("createcode_product").Where(whereMap)
	totalCount, _ := MDB.Clone().Count()
	list, err := MDB.Fields("id,title,image,cid,userType,images,likeColor,record_audio,price,des,sex,workerway,status,updatetime").Page(pageNo, pageSize).Order("id desc").Select()
	if err != nil {
		gf.Failed().SetMsg(err.Error()).Regin(c)
	} else {

		for _, val := range list {
			val["cidName"] = gf.GetTalbeFieldVal("createcode_product_cate", "name", val["cid"])
			val["userType"] = gf.GetDicFieldVal("2", val["userType"])
			if val["workerway"].String() != "" {
				val["workerway"] = gf.VarNew(gf.SplitAndStr(val["workerway"].String(), ","))
			}
		}
		gf.Success().SetMsg("获取全部列表").SetData(gf.Map{
			"page":     pageNo,
			"pageSize": pageSize,
			"total":    totalCount,
			"items":    list}).Regin(c)
	}
}

// 保存
func (api *Product) Save(c *gf.GinCtx) {
	param, _ := gf.RequestParam(c)
	var f_id = gf.GetEditId(param["id"])
	param["workerway"] = gf.ArrayToStr(param["workerway"], ",")
	if f_id == 0 {
		param["business_id"] = c.GetInt64("businessID") //当前用户商户ID
		addId, err := gf.Model("createcode_product").Data(param).InsertAndGetId()
		if err != nil {
			gf.Failed().SetMsg("添加失败").SetData(err).Regin(c)
		} else {
			if addId != 0 {
				gf.Model("createcode_product").Where("id", addId).Update(gf.Map{"weigh": addId})
			}
			gf.Success().SetMsg("添加成功!").SetData(addId).Regin(c)
		}
	} else {
		res, err := gf.Model("createcode_product").Where("business_id", c.GetInt64("businessID")).Where("id", f_id).Update(param)
		if err != nil {
			gf.Failed().SetMsg("更新失败").SetData(err).Regin(c)
		} else {
			gf.Success().SetMsg("更新成功!").SetData(res).Regin(c)
		}
	}
}

// 更新状态
func (api *Product) UpStatus(c *gf.GinCtx) {
	param, _ := gf.RequestParam(c)
	res, err := gf.Model("createcode_product").Where("business_id", c.GetInt64("businessID")).Where("id", param["id"]).Update(param)
	if err != nil {
		gf.Failed().SetMsg("更新失败!").SetData(err).Regin(c)
	} else {
		msg := "更新成功!"
		if res == nil {
			msg = "暂无数据更新"
		}
		gf.Success().SetMsg(msg).SetData(res).Regin(c)
	}
}

// 删除
func (api *Product) Del(c *gf.GinCtx) {
	param, _ := gf.RequestParam(c)
	res, err := gf.Model("createcode_product").Where("business_id", c.GetInt64("businessID")).WhereIn("id", param["ids"]).Delete()
	if err != nil {
		gf.Failed().SetMsg("删除失败").SetData(err).Regin(c)
	} else {
		gf.Success().SetMsg("删除成功!").SetData(res).Regin(c)
	}
}

// 获取内容
func (api *Product) GetContent(c *gf.GinCtx) {
	id := c.DefaultQuery("id", "")
	if id == "" {
		gf.Failed().SetMsg("请传参数id").Regin(c)
	} else {
		data, err := gf.Model("createcode_product").Where("business_id", c.GetInt64("businessID")).Where("id", id).Find()
		if err != nil {
			gf.Failed().SetMsg("获取内容失败").SetData(err).Regin(c)
		} else {
			if data != nil && data["workerway"].String() != "" {
				data["workerway"] = gf.VarNew(gf.SplitAndStr(data["workerway"].String(), ","))
			}
			gf.Success().SetMsg("获取内容成功!").SetData(data).Regin(c)
		}
	}
}

好了这就是框架设计结构与其编写业务代码流程,这里看看可以还是有点复杂,但在IED编辑器中就一目了然。

GoFly不仅是软件开发追求简单,嵌入式开发也最求简单,想学习Go语言嵌入式的可以去:嵌入式视频教程了解一下。

更多资料在:GoFly全栈开发社区 这里处理资料还可以交流学习的。

相关推荐
廖显东-ShirDon 讲编程19 小时前
《零基础Go语言算法实战》【题目 4-6】随机选择单链表的一个节点并返回
算法·程序员·go语言·web编程·go web
廖显东-ShirDon 讲编程20 小时前
《零基础Go语言算法实战》【题目 2-20】通过泛型比较大小
算法·程序员·go语言·web编程·go web
廖显东-ShirDon 讲编程2 天前
《零基础Go语言算法实战》【题目 4-8】用 Go 语言设计一个遵循最近最少使用(LRU)缓存约束的数据结构
算法·程序员·go语言·web编程·go web
廖显东-ShirDon 讲编程3 天前
《零基础Go语言算法实战》【题目 4-1】返回数组中所有元素的总和
算法·程序员·go语言·web编程·go web
廖显东-ShirDon 讲编程3 天前
《零基础Go语言算法实战》【题目 4-3】请用 Go 语言编写一个验证栈序列是否为空的算法
算法·程序员·go语言·web编程·go web
廖显东-ShirDon 讲编程4 天前
《零基础Go语言算法实战》【题目 2-22】Go 调度器优先调度问题
算法·程序员·go语言·web编程·go web
廖显东-ShirDon 讲编程5 天前
《零基础Go语言算法实战》【题目 2-7】defer 关键字特性
算法·程序员·go语言·web编程·go web
廖显东-ShirDon 讲编程5 天前
《零基础Go语言算法实战》【题目 1-14】字符串的替换
算法·程序员·go语言·web编程·go web
廖显东-ShirDon 讲编程6 天前
《零基础Go语言算法实战》【题目 2-5】函数参数的值传递和引用传递
算法·程序员·go语言·web编程·go web