用 Go 开发接口服务--公共类关键函数

栏目: Go · 发布时间: 5年前

内容简介:我们所说的公共类包,包括 dbutil、util、common 这三个包,下面展开一一说明它们里面关键的函数。**dbutil**顾名思义就是数据库工具类,我们采用了第三方 sqlx 类包来操作数据库, sqlx 的 DB 对象在 dbutil 里完成初始化工作,我们把这个简单的功能独立到一个包里,是为了以后遇到新的需求,易于扩展。比如 sqlx 有更换成 gorp、gorm 等其他第三方数据库类包,或者新增 redis 数据库的可能,到时我们只需要在这个包里更改就好。涉及更改的代码地方会大大缩小。另外独立

我们所说的公共类包,包括 dbutil、util、common 这三个包,下面展开一一说明它们里面关键的函数。

**dbutil**顾名思义就是数据库 工具 类,我们采用了第三方 sqlx 类包来操作数据库, sqlx 的 DB 对象在 dbutil 里完成初始化工作,我们把这个简单的功能独立到一个包里,是为了以后遇到新的需求,易于扩展。比如 sqlx 有更换成 gorp、gorm 等其他第三方数据库类包,或者新增 redis 数据库的可能,到时我们只需要在这个包里更改就好。涉及更改的代码地方会大大缩小。另外独立成一个包,不限于 service 服务层的其他包也可以使用。

*代码清单 - sqlx DB 对象实例化实现*

<em>package dbutil

import (
	"log"
	"time"

	_ "github.com/go-sql-driver/mysql"
	"github.com/jmoiron/sqlx"

	"chapter01/src/common"
)

var (
	// SQLXDB 声明一个 sqlx DB 实例对象
	SQLXDB *sqlx.DB
)

func init() {
	if SQLXDB != nil {
		return
	}
	SQLXDB = NewSQLXDB()
}

// NewSQLXDB 实例化一个新的 sqlx DB 实例对象
func NewSQLXDB() *sqlx.DB {
	configKit := common.ConfigKit
	if configKit == nil {
		return nil
	}

	dbDrivers, err := configKit.String("db-mysql", "db_drivers")
	if err != nil {
		log.Fatalf("无法获取配置文件的 db_drivers %#v", err)
	}
	dbConnection, err := configKit.String("db-mysql", "db_connection")
	if err != nil {
		log.Fatalf("无法获取配置文件的 db_connection %#v", err)
	}

	maxIdleConn, _ := configKit.Int("db-mysql", "db_max_idle_conn")
	maxOpenConn, _ := configKit.Int("db-mysql", "db_max_open_conn")
	connMaxLifetime, _ := configKit.Int("db-mysql", "db_conn_max_lifetime")

	db, err := sqlx.Open(dbDrivers, dbConnection)
	if err != nil {
		log.Fatalf("sqlx 初始化数据库出错:\n %#v", err)
		panic(err.Error())
	}

	db.SetMaxIdleConns(maxIdleConn)                                    
	db.SetMaxOpenConns(maxOpenConn)                                    
	db.SetConnMaxLifetime(time.Duration(connMaxLifetime) * time.Second) 

	return db
}</em>

包定义了一个全局变量 SQLXDB,包初始化的时候,检查 SQLXDB 为空,就进行初始化,初始化所需要的数据库信息,都是从配置文件 config.ini 获取的, 比如数据库 IP、用户名、密码等。

**util**包是独立于项目的类包,把它移植到其他项目照样是可以使用的。我们一般都是在项目开发过程中,不断积累此类的工具包,让这个工具包越来越完善,功能越来越强大。这个包一般存在一些 Go 内置函数的封装,或者第三方知名类包的封装。安装大业务分文件存放,比如字符串处理,文件处理,分页逻辑处理,http 处理,图片处理等等。由于 util 包涉及到的函数相当丰富,我们不能一一列举说明,我们只选择关键的函数说明一下。

*代码清单 - request 转 JSON 字符串*

// RequestJSON 直接获取 请求参数是 JSON 的字符串
func RequestJSON(req *http.Request) string {
	if req != nil && req.Body != nil {
		result, err := ioutil.ReadAll(req.Body)

		if err == nil {
			return string(result)
		}
	}
	return ""
}

*代码清单 - 发送 POST 请求的函数*

<em>// SendHTTPPost 发起 HTTP POST 请求
func SendHTTPPost(url string, param string, mime string) string {
	resp, err := http.Post(url, mime, strings.NewReader(param))
	if err != nil {
		return ""
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return ""
	}
	return string(body)
}

// SendHTTPDo 发起 HTTP Do 详细请求
func SendHTTPDo(url string, method string, params string, mime string, header map[string]string, cookie string) string {
	req, err := http.NewRequest(method, url, strings.NewReader(params))
	if err != nil {
		return ""
	}
	req.Header.Set("Content-Type", mime)
	req.Header.Set("Cookie", cookie)

	if header != nil && len(header) > 0 {
		for k, v := range header {
			req.Header.Set(k, v)
		}
	}

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return ""
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return ""
	}

	return string(body)
}</em>

*代码清单 -  文件转换 []byte 字节数组,[]byte 保存*

<em>// ReadFileToBytes 小文件推荐一次性读取,这样程序更简单,而且速度最快。
func ReadFileToBytes(filePath string) []byte {
	byt, err := ReadFile(filePath)
	if err != nil {
		log.Println(err)
		return nil
	}
	return byt
}

// BytesToFile 把字节数组转成文件
func BytesToFile(byt []byte, filename string) error {
	//如果文件夹不存在,则新建文件夹
	CheckCreatePath(GetFilePath(filename))
	return ioutil.WriteFile(filename, byt, 0777)
}</em>

*代码清单 -  图片压缩缩放剪切*

<em>// ImageResize 指定宽高比例缩放图片
func ImageResize(srcFileName string, width, height int, targetFileName string) {
	// Open the test image.
	src, err := imaging.Open(srcFileName)
	if err != nil {
		log.Fatalf("Open failed: %v", err)
	}

	// Resize the cropped image to width = 256px preserving the aspect ratio.
	dst := imaging.Resize(src, 0, height, imaging.Lanczos)

	// Save the resulting image using JPEG format.
	err = imaging.Save(dst, targetFileName)
	if err != nil {
		log.Fatalf("Save failed: %v", err)
	}
}

// ImageCut 指定宽高剪切图片
func ImageCut(srcFileName string, width, height int, targetFileName string) {
	// Open the test image.
	src, err := imaging.Open(srcFileName)
	if err != nil {
		log.Fatalf("Open failed: %v", err)
	}

	// Resize the cropped image to width = 256px preserving the aspect ratio.
	dst := imaging.Fill(src, width, height, imaging.Center, imaging.Lanczos)

	// Save the resulting image using JPEG format.
	err = imaging.Save(dst, targetFileName)
	if err != nil {
		log.Fatalf("Save failed: %v", err)
	}
}</em>

*代码清单 -  多数据分页类*

<em>// Paginate 分页结构体
type Paginate struct {
	Start     uint64 // 开始索引 从1开始
	End       uint64 // 结束索引 包括自身
	RowsCount uint64 // 总行数
}

// NewPaginate 实例化一个 Paginate 结构体
func NewPaginate(start, end uint64) *Paginate {
	if start >= end {
		start = 1
		end = 10
	}

	p := &Paginate{
		Start:     start,
		End:       end,
		RowsCount: 0,
	}
	return p
}

// GetPageIndex 获取当前页索引
func (p *Paginate) GetPageIndex() uint64 {
	return p.Start - 1
}

// GetPageSize 获取每页记录数
func (p *Paginate) GetPageSize() uint64 {
	return p.End - p.Start + 1
}

// GetTotalPage 获取总页数
func (p *Paginate) GetTotalPage() uint64 {
	if p.RowsCount > 0 {
		var t uint64
		if p.RowsCount%uint64(p.GetPageSize()) > 0 {
			t = 1
		}
		return uint64(p.RowsCount/uint64(p.GetPageSize()) + t)
	}
	return p.RowsCount
}

// GetPageCount 根据记录总数和页数尺寸,获取总页数;外部方法
func GetPageCount(rowsCount uint64, pageSize uint64) (pageCount uint64) {
	if rowsCount <= 0 || pageSize <= 0 {
		return 0
	}

	pageCount = rowsCount / pageSize
	pageRemainder := rowsCount % pageSize
	if pageCount <= 0 {
		return 1
	}

	if pageRemainder > 0 {
		return pageCount + 1
	}
	return pageCount
}</em>
分页类,传入 start 和 end 开始索引和结束索引,得到 offset 和 limit。

*代码清单 -  render 的初始化*

<em>var (
	r          *render.Render
	renderUtil *RenderUtil
)

// NewRender 实例化一个渲染类结构体
func NewRender(debug bool, templateDir string) *RenderUtil {
	renderUtil = &RenderUtil{
		debug:       debug,
		templateDir: templateDir,
	}

	return renderUtil
}

// ClassicRender 实例化一个渲染类结构体
func ClassicRender() *RenderUtil {
	return NewRender(false, "template")
}

// RenderUtil 渲染类结构体
type RenderUtil struct {
	debug       bool
	templateDir string
}

// InitRender 初始化一个 render.Render 实例
func (renderUtil *RenderUtil) InitRender() *render.Render {
	if r == nil {
		r = render.New(render.Options{
			Directory:                 renderUtil.templateDir, 
			Layout:                    "",   
			Extensions:                []string{".html", ".tmpl"}, 
			Funcs:                     []template.FuncMap{AppHelpers},  
			Delims:                    render.Delims{Left: "{{", Right: "}}"},
			Charset:                   charsetDefault,                        
			IndentJSON:                renderUtil.debug,                   
			IndentXML:                 renderUtil.debug,                  
			PrefixJSON:                []byte(""),                      
			PrefixXML:                 []byte(""),               
			HTMLContentType:           "text/html",        
			IsDevelopment:             renderUtil.debug,   
			UnEscapeHTML:              true,            
			StreamingJSON:             true,   
			RequirePartials:           true,  
			DisableHTTPErrorRendering: !renderUtil.debug,
		})
	}

	return r
}</em>

依赖 render 第三方类包,我们可以在 controller 控制层方便的输出 HTML,JSON,Text,XML 类型数据给终端。

**common**公共工具类包,和业务紧密关联,比如一些配置文件读取,错误打印,返回码和错误常量等。这些东西迁移到其他项目,可能就用不上了。以下是一些比较重要的代码。

*代码清单 -  配置文件工具类*

const (
	configPathDefault = "config.ini"
)

var (
	//ConfigKit 配置文件工具
	ConfigKit *config.Config
)

func init() {
	LoadConfigure()
}

// LoadConfigure 加载获取配置文件
func LoadConfigure() {
	if ConfigKit == nil {
		var err error
		ConfigKit, err = config.ReadDefault(configPathDefault)
		if err != nil {
			FatalErr("获取配置文件 config.ini 失败", err.Error())
		}
	}
}

//GetConfString 获取配置文件的某个字符串配置属性
func GetConfString(section, option string) string {
	v, err := ConfigKit.String(section, option)
	if err != nil {
		ShowErr(err)
		return ""
	}
	return v
}

//GetConfInt 获取配置文件的某个数值配置属性
func GetConfInt(section, option string) int {
	v, err := ConfigKit.Int(section, option)
	if err != nil {
		ShowErr(err)
		return 0
	}
	return v
}

// GetConfBool 获取配置文件的某个布朗配置属性
func GetConfBool(section, option string) bool {
	v, err := ConfigKit.Bool(section, option)
	if err != nil {
		ShowErr(err)
		return false
	}
	return v
}

*代码清单 - 错误打印类*

<em>func init() {
	logrus.SetFormatter(&logrus.TextFormatter{})
	logrus.SetReportCaller(true)

	debug := GetConfBool("default", "dev_mode")
	if debug {
		logrus.SetFormatter(&logrus.JSONFormatter{})
		logrus.SetLevel(logrus.DebugLevel)
	}
}

// FatalErr 打印致命错误,程序终止
func FatalErr(args ...interface{}) {
	if len(args) > 0 {
		logrus.Fatal(args)
	}
}


// ShowErr 打印严重错误
func ShowErr(args ...interface{}) {
	if len(args) > 0 {
		logrus.Error(args)
	}
}

// ShowDebug 打印 Debug 信息
func ShowDebug(args ...interface{}) {
	if len(args) > 0 {
		logrus.Debug(args)
	}
}

// ShowInfo 打印信息
func ShowInfo(args ...interface{}) {
	if len(args) > 0 {
		logrus.Info(args)
	}
}</em>

*代码清单 - 错误码和错误信息*

<em>const (
	// CodeSuccess 成功 code key
	CodeSuccess = 1
	// CodeFailure code key
	CodeFailure = 0
	// Code1000 code key
	Code1000 = 1000
	// Code1001 code key
	Code1001 = 1001
	// Code1002 code key
	Code1002 = 1002
	// Code1003 code key
	Code1003 = 1003
	// Code1004 code key
	Code1004 = 1004
	// Code1005 code key 仅支持POST请求!
	Code1005 = 1005

	// MsgEmpty 空字符串
	MsgEmpty = ""
	// MsgFailure 操作失败!
	MsgFailure = "操作失败!"
	// Msg1001 程序內部异常!
	Msg1001 = "程序內部异常!"
	// Msg1002 数据库异常!
	Msg1002 = "数据库异常!"
	// Msg1003 缺少参数!
	Msg1003 = "缺少参数!"
	// Msg1004 参数格式有误!
	Msg1004 = "参数格式有误!"
	// Msg1005 仅支持POST请求!
	Msg1005 = "仅支持POST请求!"
)

var (
	// CodeMsgMap map code and msg
	CodeMsgMap = map[int]string{
		CodeSuccess: MsgEmpty,
		CodeFailure: MsgFailure,
		Code1001:    Msg1001,
		Code1002:    Msg1002,
		Code1003:    Msg1003,
		Code1004:    Msg1004,
		Code1005:    Msg1005,
	}
)</em>

小结
本章节主要罗列一些关键的函数代码,关键的函数,需要项目不断的沉淀和优化完善,因为它们都是抽象出来的公共对象,很多地方引用到,特别是 util 包的函数。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

社交天性

社交天性

[美] 马修·利伯曼(Matthew D. Lieberman) / 贾拥民 / 浙江人民出版社 / 2016-6 / 69.90

[内容简介] ● 《社交天性》是社会心理学家马修·利伯曼解读人类“社会脑”的权威之作,它告诉我们为什么在充满合作与竞争的智慧社会中人们喜爱社交又相互连接,个人的社会影响力如何得以发挥,书中处处充满了令人惊喜的洞见。 ● 为什么有的人天生善于社交,而有的人总是充满障碍? 为什么智商越高的人越难相处? 心痛对人的伤害甚至超过头痛? 慈善组织如何激发人们的捐赠行为? ......一起来看看 《社交天性》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

SHA 加密
SHA 加密

SHA 加密工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具