beego一般会配合着orm这类的数据库关系映射一起使用,主要有gorm和xorm,也可以使用在其他框架内如gin框架。这里使用的orm,其实差不多触类旁通吧。
首先model层可以创建一个model.go文件也可以根据数据表来创建多个go文件,因为项目不算大,所以这里就只用一个model.go文件包含所有结构体。
在model文件中需要init函数来初始化需要连接的数据库以及创建表,项目会自动创建表,我们只需要先创建好数据库就行了。这里使用的是mysql
func init() {
// set default database 首先需要注册一个默认的数据库,这里只用到一个数据库。
dns := "root:123456@tcp(127.0.0.1:3306)/zdshop?charset=utf8mb4&parseTime=True"
_ = orm.RegisterDataBase("default", "mysql", dns)
// register model 这里仅仅是因为一行太长分开几行而已,这里需要将结构体转为数据表
orm.RegisterModel(new(User), new(Logistics), new(Information), new(Admin), new(Operation), new(Territory))
orm.RegisterModel(new(BigDistribution), new(HoldLocal), new(Distribution), new(Address), new(Goods))
orm.RegisterModel(new(GoodsCategory), new(GoodsParameter), new(ShopCart), new(GoodsBanner), new(OrderInfo), new(OrderGoods))
// create table
//当想修改表结构例如添加字段,bee run 启动项目的时候不会进行修改。
//需要第二个参数改为true,则每次加载都会重置数据库(原有数据会丢失!!)。
_ = orm.RunSyncdb("default", false, true)
}
需要注意charset后面的类型不能错,否则会报错,mysql默认的数据库类型是utf8。
同时不要忘记import
import (
"github.com/astaxie/beego/orm"
_ "github.com/go-sql-driver/mysql"
"time"
)
代码如下(示例):
//商品表
type Goods struct {
Id int
Name string `orm:"size(50);unique"` //商品名称
GoodsBrief string `orm:"type(text)"` //商品简介 数据库类型应该为blob
GoodsState int `orm:"default(0)"` //商品状态 0-上架 1-下架
Explain string `orm:"type(30)"` //说明
CreateTime time.Time `orm:"auto_now_add"` //商品录入时间
UpdateTime time.Time `orm:"auto_now"` //最后修改时间
SalesValue int `orm:"default(0)"` //销量
Category *GoodsCategory `orm:"rel(fk)"` //分类id 级联删除
ShopCart []*ShopCart `orm:"reverse(many)"`
GoodsBanner []*GoodsBanner `orm:"reverse(many)"`
GoodsParameter []*GoodsParameter `orm:"reverse(many)"`
}
通过自定义struct,使用Tag标签来确定数据类型,下面列举一些常用的
orm | sql |
---|---|
size | varchar |
unique | 唯一 |
type() | 类型(text,char) |
default() | 默认 |
rel(fk) | 外键 |
reverse(many) | 外键多对多反向关系 |
auto_now_add | 创建时间 |
auto_now | 更新时间 |
beego创建表类型默认为MyISAM,但在实际编程中需要使用到事务操作,myisam不支持事务操作,所以需要修改表引擎为InnoDB
//这个可能beego版本不同,建表默认引擎不同,没有考究过
func (g *Goods) TableEngine() string {
return "INNODB"
}
代码如下(示例):
//获取formData数据 (表单传参)
name := this.GetString("name")
id, err := this.GetInt("ID") //返回两个参数,如果接收不到ID则会返回err
this.GetFile("Img") //接收文件
this.GetStrings("id") //接收数组
//获取path数据 (url传参)
id, err := this.Int(":id") //如果没有接收到id,并不会err报错
一般查询代码如下(示例):
o := orm.NewOrm()
var goods models.Goods
goods.Id = Id
//根据Id查询所有的数据
o.read(&goods)
//如果要根据其他字段查询,例如根据name字段查询所有字段
goods.Name = name
o.read(&goods, "Name")
但是这种方法无法指定字段进行查询,也无法进行排序、过滤等操作。beego提供高级查询(增删查改)
高级查询代码如下(示例):
o := orm.NewOrm()
var good models.Goods //一条数据
var goods []*models.Goods //多条数据
//一条数据
o.QueryTalbe("Goods").需要的条件.One(&good, "需要查询指定的字段1","需要查询指定的字段2")
//多条数据
o.QueryTalbe("Goods").需要的条件.All(&goods, "需要查询指定的字段1","需要查询指定的字段2")
下面列举几个常用的条件
.条件 | sql |
---|---|
Filter() | where |
orderBy() | 排序 |
Limit () | Limit |
RelatedSel() | 关联查询 |
o.QueryTable("GoodsParameter").Filter("Goods__Id", id).All(¶meters)
代码如下(示例):
o := orm.NewOrm()
var goods models.goods
goods.Id = 1
goods.Name = "huawei"
//添加外键关联商品分类id, 不能直接goods.Category = 1, 会报错
var category models.GoodsCategory
category.Id = categoryId
goods.Category = &category
o.Insert(&goods)
方法一: 代码如下(示例):
o := orm.NewOrm()
var goods models.Goods
goods.Id = id
goods.Name = name
goods.GoodsBrief = brief
//这里虽然在model中time的tag不一样,但是没办法做到自动更新时间,需要指定updateTime
_, err = o.Update(&goods, "Name", "GoodsBrief", "UpdateTime")
//下面这个更新如果没有指定字段的话,会设置为null,但是model中建表时默认不为空,所以无法这样更新
_, err = o.Update(&goods)
方法二:(推荐这个)代码如下(示例):
o.QueryTalbe("表名").Filter("id", 1).Update(orm.Params{
"name": "",
"GoodsBrief": "",
})
代码如下(示例):
_, err := o.QueryTable("表名").Filter("id", 1).Delete()
下面这个我找到一个queryTable接口博客,比我的详细,
链接: queryTable接口.
swagger是一个go语言和框架结合使用的自动化API文档。通过注释来完成api文档的编辑,相当好用。
还实现了restful api 接口规范。
代码如下(示例):
// @Title Post
// @Description AddGoods and AddGoodsParameter and AddGoodsBanner
// @Param name formData true "商品名称"
// @Param brief formData true "商品简介"
// @Param state formData true "商品状态 1-上架 2-下架"
// @Param explain formData true "商品说明"
// @Param categoryId formData true "商品分类id"
// @Param parameters formData true "商品参数"
// @Param img formData true "商品默认图片"
// @Param imgs formData false "商品轮播图片"
// @Success 200 {string} 商品添加成功
// @Failure 400 商品添加失败
// @router / [post]
func (this *GoodsController) Post() {...}
启动项目,访问http://127.0.0.1:8080/swagger/ 效果如下:
参考链接: swagger文档
这里在网上找到了一个beego的日志收集,出处找不到了,所以直接将源代码放出来。
package utils
import (
"encoding/json"
"fmt"
"github.com/astaxie/beego"
"github.com/astaxie/beego/config"
"github.com/astaxie/beego/logs"
)
//说明:日志记录
//在conf下app.conf中进行配置, 只保存最近10个文件,会不停覆盖。最新记录在app.log中
//[log]
//log_level = 7
//log_path = logs/app.log
//maxlines = 100 表示每个文件最大行数
//maxsize = 1024 表示每个文件最大大小
/*
在main函数中进行调用
func init() {
//日志写入
_ = utils.InitLogger()
}
*/
// RFC5424 log message levels.
const (
LevelEmergency = iota
LevelAlert //1
LevelCritical //2
LevelError //3
LevelWarning //4
LevelNotice //5
LevelInformational//6
LevelDebug //7
)
func InitLogger() (err error) {
BConfig, err := config.NewConfig("ini", "conf/app.conf")
if err != nil{
fmt.Println("config init error:", err)
return
}
maxlines, lerr := BConfig.Int64("log::maxlines")
if lerr != nil {
maxlines = 1000
}
logConf := make(map[string]interface{})
logConf["filename"] = BConfig.String("log::log_path")
level,_ := BConfig.Int("log::log_level")
logConf["level"] = level
logConf["maxlines"] = maxlines
confStr, err := json.Marshal(logConf)
if err != nil {
fmt.Println("marshal failed,err:", err)
return
}
//设置输出到文件
//beego.SetLogger("file", `{"filename":"logs/test.log"}`)
beego.SetLogger(logs.AdapterFile, string(confStr))
//输是否出文件名和行号
beego.SetLogFuncCall(true)
//设置级别设置日志级别
//beego.SetLevel(beego.LevelInformational)
return
}
创建一个log文件夹,当运行时,所有的请求和beego.Info都会写到log文件夹下的日志当中
自己写的一个公共方法
//调用
/*
//第二个参数为formData的key,第三个参数为限制图片上传的最大大小, 200000为2M
errMsg, url := utils.HandleFile(&this.Controller, "img", 200000)
if errMsg != "" {
beego.Info(errMsg)
return
}
*/
//文件上传
func HandleFile(this *beego.Controller, image string, size int64) (error, url string) {
//headers, err := this.GetFiles(image)
file, header, err := this.GetFile(image)
defer file.Close()
if err != nil {
return "图片上传失败", ""
}
//大小
if header.Size > size {
return "图片太大无法上传", ""
}
//格式
ext := path.Ext(header.Filename)
if ext != ".jpg" && ext != ".png" && ext != ".jpeg" {
beego.Info("图片格式不正确")
return "图片格式不正确", ""
}
//防重名
fileName := strconv.Itoa(int(time.Now().UnixNano())) + ext
//存储 第一个参数要和GetFile的一样
err = this.SaveToFile(image, "./upload/img/"+fileName)
if err != nil {
return "文件存储错误 ", ""
}
return "", "./upload/img/"+fileName
}
下面说说我在写项目时遇到一些问题以及解决方法。
var carts []Cart
for key, value := range goodsParaMap {
var cart model.Cart
...
//应该是后添加的商品在前面显示
//下面这个可以实现在slice头部追加元素
carts = append([]Cart{cart}, carts...)
}
当想对一个slice追加一个slice的时候,第二个参数即要追加的slice,用…来展开追加
接收:
data := this.GetString("parameters")
beego.Info("json: ", data)
var parameters []JsonPara
err = json.Unmarshal([]byte(data), ¶meters)
if err != nil {
beego.Info("json.Unmarshal err: ", err)
beego.Info("传入数据有误") //不能为空且不能类型不对,否则无法unmarshal
return
}
返回:
resp := make(map[string]interface{})
defer this.ServeJSON()
resp["key1"] = value1
resp["key2"] = value2
this.Data["json"] = resp
o := orm.NewOrm()
//事务开始
o.Begin()
//事务回滚
o.Rollback()
//事务提交
o.Commit()
如果不进行格式化输出,是没办法直接获取到下面这种时间类型的
//返回数据库格式化时间:2020-12-16 21:24:35
createTime := goods.CreateTime.Format("2006-01-02 15:04:05")
beego其实还有一些功能但我没有用上,例如可以在controller中定义表单对应的struct和tag来进行自动校验。
在写项目的时候也遇上很多问题,查了很多资料,下面给几个我经常看的链接,都挺好的。
参考链接:
beego脱坑
queryTable接口.
git使用 这是我看过git教学最好的一篇博客
golang基础教程 这是李文周老师的博客 我觉得很好, 推荐给大家
beego的路由很简单,没有什么好说的。如果使用swagger创建beego项目还有自带的示例,相当友好
启动项目:
bee run
有时候会报错,可以用这个再次生成文档
bee run -gendoc=true -downdoc=true
这是第一次使用beego写项目,因此想对此总结一下,beego该有的都有,github上也有beego的支付宝微信支付源码,一样可以连接redis;不要看现在做go语言开发的少,go在服务器开发也很有优势,给我最大的感觉是效率高。之前一直使用php的TP5来写服务端,用完beego感觉会更加简单,代码更加清晰,相对代码行数也少了很多。个人的话更加倾向于beego。beego其实也可以做更大的项目,业务逻辑复杂还可以自己添加逻辑层以及dao层。beego当然也有缺陷,由于技术相对较新,一些问题网上搜了很久也没有答案;希望golang越来越棒吧。