留子厨房开发日志

以下记录了使用go语言框架Beego,Mysql数据库,Redis数据库实现一个点菜/菜谱应用API的全过程。

技术方案

 github地址

数据库设计

新建数据库:

CREATE DATABASE menu;

新建数据表:

CREATE TABLE `menu` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(200) NOT NULL DEFAULT '',
`content` text NOT NULL,
`pictures` varchar(1000) NOT NULL DEFAULT '',
`tags` varchar(1000) NOT NULL DEFAULT '',
`status` tinyint(4) NOT NULL DEFAULT '0',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `data` (`id` int(10) unsigned NOT NULL AUTO_INCREMENT,`menu_id` int(10) unsigned NOT NULL DEFAULT '0',`like_number` int(10) NOT NULL DEFAULT '0',`visit_number` int(10) NOT NULL DEFAULT '0',`order_number` int(10) NOT NULL DEFAULT '0',`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,PRIMARY KEY (`id`),UNIQUE KEY `unique_menu` (`menu_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
CREATE TABLE `order_info` (`id` int(10) unsigned NOT NULL AUTO_INCREMENT,`menu_id_list` varchar(200) NOT NULL DEFAULT '',`status` tinyint(4) NOT NULL DEFAULT '0',`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

生成models层

进入终端项目,执行命令生成数据库表对应的models:

bee generate appcode -tables="data" -driver=mysql -conn="root:123456@tcp(127.0.0.1:3306)/menu" -level=1
bee generate appcode -tables="order_info" -driver=mysql -conn="root:123456@tcp(127.0.0.1:3306)/menu" -level=1
bee generate appcode -tables="menu" -driver=mysql -conn="root:123456@tcp(127.0.0.1:3306)/menu" -level=1

 生成data、menu、order_info对应的model文件

我们数据库设置了创建时间和更新时间的自动更新

配置环境变量连接数据库:

conf/app.conf中添加:

# Mysql setting
mysqluser = "root"
mysqlpass = "root"
mysqlhost = "127.0.0.1"
mysqldb   = "liuyan"
mysqlport = 3306

 新建core文件,用于数据库连接的核心代码:

package modelsimport ("fmt""github.com/beego/beego/v2/client/orm"beego "github.com/beego/beego/v2/server/web"_ "github.com/go-sql-driver/mysql"
)func Init() {username, err := beego.AppConfig.String("mysqlname")if err != nil {fmt.Println("err:", err)}password, err := beego.AppConfig.String("mysqlpass")host, err := beego.AppConfig.String("mysqlhost")database, err := beego.AppConfig.String("mysqldb")port, err := beego.AppConfig.String("mysqlport")dsn := username + ":" + password + "@tcp(" + host + ":" + port + ")/" + database + "?charset=utf8&loc=Local"err = orm.RegisterDataBase("default", "mysql", dsn)if err != nil {fmt.Println("err::", err)}
}

之后在main.go中初始化数据库连接:

package mainimport ("menu_api/models"_ "menu_api/routers"beego "github.com/beego/beego/v2/server/web"
)func init() {models.Init()
}func main() {if beego.BConfig.RunMode == "dev" {beego.BConfig.WebConfig.DirectoryIndex = truebeego.BConfig.WebConfig.StaticDir["/swagger"] = "swagger"}beego.Run()
}

bee run .

没有打印错误,则说明数据库连接成功 

日志设置

新建logs/logs.go文件进行初始化日志:

package logsimport ("github.com/beego/beego/v2/core/logs"
)func Init() {// 设置日志模式为文件,输出的目录logs.SetLogger("file", `{"filename":"logs/menu.log"}`)
}

将日志模式设置为文件格式,输出的目录为logs/menu.log(无需手动创建)

在main.go中初始化日志:

bee run . 后会发现新建了文件logs/menu.log,并且可以在其中发现项目打印的日志,关于日志,可以参考官方文档:Welcome to Beego | Beego

核心逻辑

菜单逻辑

注册路由

新增逻辑:

beego.NSNamespace("/menu",beego.NSInclude(&controllers.MenuController{},),),

models层已经自动构建,只需要实现controller即可

新建controller/menu.go

查询列表,查询详情,增加,更新方法:

package controllersimport ("encoding/json""errors""github.com/beego/beego/v2/core/logs"beego "github.com/beego/beego/v2/server/web""menu_api/models""strings"
)type MenuController struct {beego.Controller
}// @Title GetList
// @Description get Menu List
// @Param   query   query   string  false   "Filter. e.g. col1:v1,col2:v2 ..."
// @Param   fields  query   string  false   "Fields returned. e.g. col1,col2 ..."
// @Param   sortby  query   string  false   "Sorted-by fields. e.g. col1,col2 ..."
// @Param   order   query   string  false   "Order corresponding to each sortby field, if single value, apply to all sortby fields. e.g. desc,asc ..."
// @Param   limit   query   string  false   "Limit the size of result set. Must be an integer"
// @Param   offset  query   string  false   "Start position of result set. Must be an integer"
// @Success 200 {object} models.Menu
// @router / [get]
func (m *MenuController) GetList() {var fields []stringvar sortby []stringvar order []stringvar query = make(map[string]string)var limit int64 = 10var offset int64// fields: col1,col2,entity.col3if v := m.GetString("fields"); v != "" {fields = strings.Split(v, ",")}// limit: 10 (default is 10)if v, err := m.GetInt64("limit"); err == nil {limit = v}// offset: 0 (default is 0)if v, err := m.GetInt64("offset"); err == nil {offset = v}// sortby: col1,col2if v := m.GetString("sortby"); v != "" {sortby = strings.Split(v, ",")}// order: desc,ascif v := m.GetString("order"); v != "" {order = strings.Split(v, ",")}// query: k:v,k:vif v := m.GetString("query"); v != "" {for _, cond := range strings.Split(v, ",") {kv := strings.SplitN(cond, ":", 2)if len(kv) != 2 {m.Data["json"] = errors.New("Error: invalid query key/value pair")m.ServeJSON()return}k, v := kv[0], kv[1]query[k] = v}}menList, err := models.GetAllMenu(query, fields, sortby, order, offset, limit)if err != nil {logs.Error("Get Database Menu List Error, ERR:", err)m.Data["json"] = errors.New("Error: Get Database Menu Error")}m.Data["json"] = menListm.ServeJSON()
}// @Title Get
// @Description get menu by id
// @Param	id		path 	int	true		"The key for staticblock"
// @Success 200 {object} models.Menu
// @Failure 403 :id is empty
// @router /:id [get]
func (m *MenuController) Get() {mid := 0if v, err := m.GetInt(":id"); err == nil {mid = v}if mid != 0 {menu, err := models.GetMenuById(mid)if err != nil {logs.Error("Get Database Menu Error, ERR:", err)m.Data["json"] = err.Error()} else {m.Data["json"] = menu}}m.ServeJSON()
}// @Title CreateMenu
// @Description create menus
// @Param	body		body 	models.Meau	true		"body for user content"
// @Success 200 {int} models.Menu.Id
// @Failure 403 body is empty
// @router / [post]
func (m *MenuController) Post() {var menu models.Menuerr := json.Unmarshal(m.Ctx.Input.RequestBody, &menu)if err != nil || menu.Title == "" || menu.Content == "" || menu.Pictures == "" {if err != nil {logs.Error("Add Database Menu Unmarshal Error, ERR:", err)} else {err = errors.New("title or content or picture is not empty")}m.Data["json"] = err.Error()m.ServeJSON()return}mid, err := models.AddMenu(&menu)if err != nil {logs.Error("Add Database Menu Error, ERR:", err)m.Data["json"] = err.Error()m.ServeJSON()return}m.Data["json"] = map[string]int64{"mid:": mid}m.ServeJSON()
}// @Title Update
// @Description update the menu
// @Param	id		path 	string	true		"The menu_id you want to update"
// @Param	body		body 	models.Menu	true		"body for user content"
// @Success 200 {object} models.Menu
// @Failure 403 :id is not int
// @router /:id [put]
func (m *MenuController) Put() {mid, err := m.GetInt(":id")var menu models.Menuerr = json.Unmarshal(m.Ctx.Input.RequestBody, &menu)if err != nil {logs.Error("Add Database Menu Error, ERR:", err)m.Data["json"] = err.Error()m.ServeJSON()return}menu.Id = midif mid != 0 {err := models.UpdateMenuById(&menu)if err != nil {m.Data["json"] = err.Error()} else {m.Data["json"] = "update success"}}m.ServeJSON()
}

订单逻辑

注册路由

新增逻辑:

		beego.NSNamespace("/order_info",beego.NSInclude(&controllers.OrderInfoController{},),),

新建controller/order_info.go

查询列表,查询详情,增加,更新方法:

package controllersimport ("encoding/json""errors""github.com/beego/beego/v2/core/logs"beego "github.com/beego/beego/v2/server/web""menu_api/models""strings"
)type OrderInfoController struct {beego.Controller
}// @Title GetList
// @Description get OrderInfo List
// @Param   query   query   string  false   "Filter. e.g. col1:v1,col2:v2 ..."
// @Param   fields  query   string  false   "Fields returned. e.g. col1,col2 ..."
// @Param   sortby  query   string  false   "Sorted-by fields. e.g. col1,col2 ..."
// @Param   order   query   string  false   "Order corresponding to each sortby field, if single value, apply to all sortby fields. e.g. desc,asc ..."
// @Param   limit   query   string  false   "Limit the size of result set. Must be an integer"
// @Param   offset  query   string  false   "Start position of result set. Must be an integer"
// @Success 200 {object} models.OrderInfo
// @router / [get]
func (o *OrderInfoController) GetList() {var fields []stringvar sortby []stringvar order []stringvar query = make(map[string]string)var limit int64 = 10var offset int64// fields: col1,col2,entity.col3if v := o.GetString("fields"); v != "" {fields = strings.Split(v, ",")}// limit: 10 (default is 10)if v, err := o.GetInt64("limit"); err == nil {limit = v}// offset: 0 (default is 0)if v, err := o.GetInt64("offset"); err == nil {offset = v}// sortby: col1,col2if v := o.GetString("sortby"); v != "" {sortby = strings.Split(v, ",")}// order: desc,ascif v := o.GetString("order"); v != "" {order = strings.Split(v, ",")}// query: k:v,k:vif v := o.GetString("query"); v != "" {for _, cond := range strings.Split(v, ",") {kv := strings.SplitN(cond, ":", 2)if len(kv) != 2 {o.Data["json"] = errors.New("Error: invalid query key/value pair")o.ServeJSON()return}k, v := kv[0], kv[1]query[k] = v}}orderList, err := models.GetAllOrderInfo(query, fields, sortby, order, offset, limit)if err != nil {logs.Error("Get Database OrderInfo List Error, ERR:", err)o.Data["json"] = errors.New("Error: Get Database OrderInfo Error")}o.Data["json"] = orderListo.ServeJSON()
}// @Title Get
// @Description get orderInfo by id
// @Param	id		path 	int	true		"The key for staticblock"
// @Success 200 {object} models.OrderInfo
// @Failure 403 :id is empty
// @router /:id [get]
func (o *OrderInfoController) Get() {oid := 0if v, err := o.GetInt(":id"); err == nil {oid = v}if oid != 0 {orderInfo, err := models.GetOrderInfoById(oid)if err != nil {logs.Error("Get Database OrderInfo Error, ERR:", err)o.Data["json"] = err.Error()} else {o.Data["json"] = orderInfo}}o.ServeJSON()
}// @Title CreateOrderInfo
// @Description create OrderInfo
// @Param	body		body 	models.OrderInfo	true		"body for user content"
// @Success 200 {int} models.OrderInfo.Id
// @Failure 403 body is empty
// @router / [post]
func (o *OrderInfoController) Post() {var orderInfo models.OrderInfoerr := json.Unmarshal(o.Ctx.Input.RequestBody, &orderInfo)if err != nil || orderInfo.MenuIdList == "" {if err != nil {logs.Error("Add Database OrderInfo Unmarshal Error, ERR:", err)} else {err = errors.New("title or content or picture is not empty")}o.Data["json"] = err.Error()o.ServeJSON()return}oid, err := models.AddOrderInfo(&orderInfo)if err != nil {logs.Error("Add Database OrderInfo Error, ERR:", err)o.Data["json"] = err.Error()o.ServeJSON()return}o.Data["json"] = map[string]int64{"oid:": oid}o.ServeJSON()
}// @Title Update
// @Description update the OrderInfo
// @Param	id		path 	string	true		"The menu_id you want to update"
// @Param	body		body 	models.OrderInfo	true		"body for user content"
// @Success 200 {object} models.OrderInfo
// @Failure 403 :id is not int
// @router /:id [put]
func (o *OrderInfoController) Put() {mid, err := o.GetInt(":id")var orderInfo models.OrderInfoerr = json.Unmarshal(o.Ctx.Input.RequestBody, &orderInfo)if err != nil {logs.Error("Add Database OrderInfo Error, ERR:", err)o.Data["json"] = err.Error()o.ServeJSON()return}orderInfo.Id = midif mid != 0 {err := models.UpdateOrderInfoById(&orderInfo)if err != nil {o.Data["json"] = err.Error()} else {o.Data["json"] = "update success"}}o.ServeJSON()
}

 ​​​​​​

数据更新

使用redis缓存+定时任务的方式维护data数据库,先存储在redis中,再由定时脚本进行更新(同步redis数据到mysql中)。

1.安装redis包

go get github.com/gomodule/redigo/redis

2.配置环境变量

redis_host = localhost
redis_port = 6379
redis_password = 
redis_db = 0

3.redis初始化连接


func RedisContent() redis.Conn {redis_host, err := beego.AppConfig.String("redis_host")redis_port, err := beego.AppConfig.String("redis_port")redis_password, err := beego.AppConfig.String("redis_password")redis_db, err := beego.AppConfig.String("redis_db")if err != nil {logs.Error("database get appConfig err", err)}Redis_pool := &redis.Pool{MaxIdle:     1,                 //最大空闲连接数MaxActive:   10,                // 最大连接数IdleTimeout: 180 * time.Second, //空闲连接超时时间Wait:        true,              // 超过最大连接数的操作:等待Dial: func() (redis.Conn, error) {c, err := redis.Dial("tcp", fmt.Sprintf("%s:%s", redis_host, redis_port))if err != nil {return nil, err}if redis_password != "" {if _, err := c.Do("AUTH", redis_password); err != nil {c.Close()return nil, err}}if redis_db != "" {if _, err := c.Do("SELECT", redis_db); err != nil {c.Close()return nil, err}}return c, nil},}return Redis_pool.Get()
}

新建controller/common.go文件

逻辑:redis的菜单ID的key加一,并且添加到redis的菜单ID列表。

这个文件存放公共的function:

package controllersimport (beego "github.com/beego/beego/v2/server/web""menu_api/models""time"
)const (OptLikeNum  = "like_num"OptOrderNum = "order_num"OptVisitNum = "visit_num"
)func getRedisKey() (string, string, error) {// 获取key配置numKey, err := beego.AppConfig.String("redis_menu_num_key")if err != nil {return "", "", err}updateKey, err := beego.AppConfig.String("redis_menu_update_key")if err != nil {return "", "", err}return numKey, updateKey, nil
}func redisNumUpdate(mid string, operateType string) (error, interface{}) {numKey, updateKey, err := getRedisKey()if err != nil {return err, nil}// like_number++conn, err := models.RedisContent()if err != nil {return err, nil}defer conn.Close()newValue, err := conn.Do("HINCRBY", numKey+mid, operateType, 1)if err != nil {return err, nil}// TODO 确定是否为第一次+1_, err = conn.Do("SADD",updateKey+time.Now().Format("20060102"), mid)if err != nil {return err, nil}return nil, newValue
}

点赞数量更新redis

注册路由:

新增逻辑:

beego.NSNamespace("/data",beego.NSInclude(&controllers.DataController{},),),

新建controller/data.go文件

实现点赞数量存储:

package controllersimport ("github.com/beego/beego/v2/core/logs"beego "github.com/beego/beego/v2/server/web"_ "menu_api/models"
)type DataController struct {beego.Controller
}// @Title Update
// @Description update the menu
// @Param	menu_id		path 	string	true		"The menu_id you want to update"
// @Success 200 {int} 1
// @Failure 403 :menu_id is not int
// @router /:menu_id [put]
func (d *DataController) Put() {mid := d.GetString(":menu_id")// 更细rediserr, num := redisNumUpdate(mid, OptLikeNum)if err != nil {logs.Error("Get AppConfig Error, ERR:", err)d.Data["json"] = err.Error()d.ServeJSON()return}d.Data["json"] = numd.ServeJSON()
}

更新订单数量和浏览量

浏览量主要在菜单详情接口中,点击一次+1;

	err, _ := redisNumUpdate(strconv.Itoa(mid), OptBrowseNum)if err != nil {logs.Error("Get AppConfig Error, ERR:", err)// 不影响主流程,不return}

订单数量主要在订单添加接口中,下单一次+1;

下单在更新接口中,改订单状态为已下单:

// @Title Update
// @Description update the OrderInfo
// @Param	id		path 	string	true		"The menu_id you want to update"
// @Param	body		body 	models.OrderInfo	true		"body for user content"
// @Success 200 {object} models.OrderInfo
// @Failure 403 :id is not int
// @router /:id [put]
func (o *OrderInfoController) Put() {oId, err := o.GetInt(":id")var orderInfo models.OrderInfoerr = json.Unmarshal(o.Ctx.Input.RequestBody, &orderInfo)if err != nil {logs.Error("Add Database OrderInfo Error, ERR:", err)o.Data["json"] = err.Error()o.ServeJSON()return}orderInfo.Id = oIdif oId != 0 {err := models.UpdateOrderInfoById(&orderInfo)if err != nil {o.Data["json"] = err.Error()} else {o.Data["json"] = "update success"}}if orderInfo.Status == 1 {// 遍历菜单列表,对每个菜进行加一操作menuIdList := strings.Split(orderInfo.MenuIdList, ",")for _, menuId := range menuIdList {err, _ = redisNumUpdate(menuId, OptOrderNum)if err != nil {logs.Error("Get AppConfig Error, ERR:", err)// 不影响主流程,不return}}}o.ServeJSON()
}

同步redis数据到数据库

定时任务


func SyncDataFromRedisToMysql() error {numKey, updateKey, err := getRedisKey()if err != nil {return err}// like_number++conn, err := models.RedisContent()if err != nil {return err}defer conn.Close()// 获取集合数据menuIds, err := redis.Strings(conn.Do("SMEMBERS", updateKey+time.Now().Format("20060102")))if err != nil {return err}for _, menuId := range menuIds {// 根据Id获取数据var fields []stringvar sortby []stringvar order []stringvar query = map[string]string{"MenuId": menuId,}var limit int64 = 10var offset int64ml, err := models.GetAllData(query, fields, sortby, order, offset, limit)if err != nil {return err}// 根据menuId获取更新mysql数据values, err := redis.StringMap(conn.Do("HGETALL", numKey+menuId))if err != nil {return err}// 先marshal,再Unmarshalvar data models.Dataif len(ml) > 0 && ml != nil {marshal, err := json.Marshal(ml[0])if err != nil {return err}err = json.Unmarshal((marshal), &data)}likeNum, err := strconv.Atoi(values[OptLikeNum])orderNum, err := strconv.Atoi(values[OptOrderNum])visitNum, err := strconv.Atoi(values[OptVisitNum])data.LikeNumber = likeNumdata.OrderNumber = orderNumdata.VisitNumber = visitNum// 如果找到则更新,找不到则添加if len(ml) < 1 || ml == nil {mId, _ := strconv.Atoi(menuId)data.MenuId = mId_, err = models.AddData(&data)} else {err = models.UpdateDataById(&data)}if err != nil {return err}}return nil
}

main.go中设置协程,五分钟刷新一次:

func init() {logs.Init()models.Init()controllers.SyncDataFromRedisToMysql()go func() {for {// 每隔五分钟执行一次同步方法time.Sleep(5 * time.Minute)err := controllers.SyncDataFromRedisToMysql()if err != nil {beeLogs.Error("Data SyncDataFromRedisToMysql Error:", err)} else {beeLogs.Info(time.Now(), "SyncDataFromRedisToMysql Success")}}}()
}

文件上传

需要上传图片到服务器

注册路由

新增逻辑:

beego.NSNamespace("/common",beego.NSInclude(&controllers.CommonController{},),),

controller/commpn.go中新增逻辑

// @router /upload [post]
// @Summary 上传图片
// @Description 上传图片到服务器
// @Accept multipart/form-data
// @Param image formData file true "图片文件"
// @Success 200 {string} success "上传成功"
// @Failure 400 {string} error "上传失败"
// @router /upload [post]
func (c *CommonController) Post() {f, h, err := c.GetFile("image")if err != nil {c.Ctx.WriteString("File upload failed: " + err.Error())return}ext := path.Ext(h.Filename)//验证后缀名是否符合要求var AllowExtMap map[string]bool = map[string]bool{".jpg":  true,".jpeg": true,".png":  true,}if _, ok := AllowExtMap[ext]; !ok {c.Ctx.WriteString("后缀名不符合上传要求")return}//创建目录uploadDir := "static/upload/"//构造文件名称rand.Seed(time.Now().UnixNano())randNum := fmt.Sprintf("%d", rand.Intn(9999)+1000)hashName := md5.Sum([]byte(time.Now().Format("2006_01_02_15_04_05_") + randNum))fileName := fmt.Sprintf("%x", hashName) + ext//c.Ctx.WriteString(  fileName )fpath := uploadDir + fileNamedefer f.Close() //关闭上传的文件,不然的话会出现临时文件不能清除的情况err = c.SaveToFile("image", fpath)if err != nil {c.Ctx.WriteString(fmt.Sprintf("%v", err))}}

以上就是整体服务端的实现。

运行

生成路由 

bee generate routers

生成swagger配置文件,并运行

bee run -gendoc=true

访问swagger

http://127.0.0.1:8080/swagger/#/

接口如下:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/699112.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2024 CKS 题库 | 11、AppArmor

不等更新题库 CKS 题库 11、AppArmor Context: APPArmor 已在 cluster 的工作节点node02上被启用。一个 APPArmor 配置文件已存在&#xff0c;但尚未被实施。 Task: 在 cluster 的工作节点node02上&#xff0c;实施位于 /etc/apparmor.d/nginx_apparmor 的现有APPArmor 配置…

Python 实现 ADTM 指标计算:股票技术分析的利器系列(9)

Python 实现 ADTM 指标计算&#xff1a;股票技术分析的利器系列&#xff08;9&#xff09; 介绍算法解释 核心代码rolling函数介绍计算 DTMnp.where 使用介绍np.maximum 计算 DBM计算 STM计算 SBM计算 ADTM 完整代码 介绍 ADTM&#xff08;动态买卖气指标&#xff09;是一种用…

C++奇怪的 ::template

答疑解惑 怎么会有::template的写法 起初 在阅读stl的源码的时候&#xff0c;发现了一条诡异的代码 // ALIAS TEMPLATE _Rebind_alloc_t template<class _Alloc,class _Value_type> using _Rebind_alloc_t typename allocator_traits<_Alloc>::template rebind…

【misc | CTF】攻防世界 simple_transfer

天命&#xff1a;这题其实不简单啊 拿到流量包&#xff0c;丢进去wireshare&#xff0c;题目都说了flag在里面 ctrl f 直接搜索字符串 右键&#xff0c;追踪流 -> TCP流 查找 .pdf 文件&#xff0c;其实这里思路是比较奇怪的&#xff0c;毕竟是的确比较多内容&#xff0c…

基于PostGIS的慢查询引起的空间索引提升实践

目录 前言 一、问题定位 1、前端接口定位 2、后台应用定位 3、找到问题所在 二、空间索引优化 1、数据库查询 2、创建空间索引 3、geography索引 4、再看前端响应 总结 前言 这是一个真实的案例&#xff0c;也是一个新入门的工程师很容易忽略的点。往往在设计数据库的…

NestJS入门4:MySQL typeorm 增删改查

前文参考&#xff1a; NestJS入门1 NestJS入门2&#xff1a;创建模块 NestJS入门3&#xff1a;不同请求方式前后端写法 1. 安装数据库相关模块 npm install nestjs/typeorm typeorm mysql -S 2. MySql中创建数据库 ​ 3. 添加连接数据库代码 app.module.ts ​ import { M…

给自己留个备忘,blender是右手坐标系

所谓右手坐标系&#xff0c;就是三个轴的方向和右手三根手指的方向一致&#xff08;当然&#xff0c;有要求的&#xff0c;这个要求是大拇指指向x轴方向&#xff0c;食指指向y轴方向,中指指向z轴方向&#xff09;。 不过blender默认是z轴朝上的&#xff0c;如下图。 右手坐标系…

element导航菜单el-menu添加搜索功能

element导航菜单-侧栏&#xff0c;自带的功能没有搜索或者模糊查询。 找了找资料 找到一个比较可行的&#xff0c;记录一下&#xff1a; //index.vue的代码 <div style"overflow:auto"><el-menu :default-active"$route.path":default-openeds&…

<网络安全>《49 网络攻防专业课<第十三课 - 华为防火墙的使用(2)>

6 防火墙的防范技术 6.1 ARP攻击防范 攻击介绍 攻击者通过发送大量伪造的ARP请求、应答报文攻击网络设备&#xff0c;主要有ARP缓冲区溢出攻击和ARP拒绝服务攻击两种。 ARP Flood攻击&#xff08;ARP扫描攻击&#xff09;&#xff1a;攻击者利用工具扫描本网段或者跨网段主机时…

构造器详解

定义: 是一种特殊类型的方法&#xff0c;用于创建对象时初始化对象的状态。 使用new关键字创建对象 构造器特点: 1.和类名相同 2.没有返回值 public class Person {String name;public Person() {this.name"John";}}public class Test {public static void main…

vue2+element医院安全(不良)事件报告管理系统源代码

目录 安全不良事件类型 源码技术栈 医院安全&#xff08;不良&#xff09;事件报告管理系统采用无责的、自愿的填报不良事件方式&#xff0c;有效地减轻医护人员的思想压力&#xff0c;实现以事件为主要对象&#xff0c;可以自动、及时、实际地反应医院的安全、不良、近失事件…

测试环境搭建整套大数据系统(六:搭建sqoop)

一&#xff1a;下载安装包 https://archive.apache.org/dist/sqoop/ 二&#xff1a;解压修改配置。 tar -zxvf sqoop-1.4.7.bin__hadoop-2.6.0.tar.gz -C /opt cd /opt mv sqoop-1.4.7.bin__hadoop-2.6.0/ sqoop-1.4.7修改环境变量 vi /etc/profile#SQOOP_HOME export SQOOP_…

nginx-------- 高性能的 Web服务端 (四)

一、高级配置 1 .1网页的状态页 基于nginx 模块 ngx_http_stub_status_module 实现&#xff0c;在编译安装nginx的时候需要添加编译参数 --with-http_stub_status_module&#xff0c;否则配置完成之后监测会是提示语法错误注意: 状态页显示的是整个服务器的状态,而非虚拟主机…

【Android 性能优化:内存篇】——ExoPlayer 释放后内存没有恢复问题探索

背景 最近笔者承接项目的内存优化指标&#xff0c;在内存调研的过程中发现项目中视频播放结束后&#xff0c;内存没有恢复到播放前到水平。项目中用的 EXO 版本为2.19.1&#xff0c;并且笔者自己也写了个简单的 Demo&#xff0c;发现也是如此。虽然有一些偏门方法可以优化&…

4 buuctf解题

[CISCN 2019 初赛]Love Math1 打开题目 题目源码 <?php error_reporting(0); //听说你很喜欢数学&#xff0c;不知道你是否爱它胜过爱flag if(!isset($_GET[c])){show_source(__FILE__); }else{//例子 c20-1$content $_GET[c];if (strlen($content) > 80) {die("…

无人机的视频图传技术

在操控无人机时&#xff0c;视频图传技术显得尤为关键。通过这项技术&#xff0c;无人机的摄像头所捕捉的画面能实时回传至遥控器&#xff0c;使操作者全面掌握无人机的拍摄情况。同时&#xff0c;无人机图传技术也是衡量无人机性能的重要标准&#xff0c;它关乎飞行距离与时间…

【Vuforia+Unity】AR07-实现识别条码、二维码内容功能(Barcode Scanner)

Barcode Scanner in Unity | Vuforia Library官方教程&#xff0c;写的很详细&#xff0c;本教程主要参考对象&#xff01; 主要实现扫描生活中常见的二维码&#xff0c;然后弹出二维码链接&#xff0c;当然我们也可以再次回调自定义函数&#xff0c;弹出数字内容&#xff0c;…

编译GreatSQL with RocksDB引擎

GreatSQL里也能用上RocksDB引擎 1. 前言 RocksDB 是基于Facebook 开源的一种支持事务的、高度可压缩、高性能的MyRocks存储引擎&#xff0c;特别适用于高度压缩和大容量的数据。以下是一些关键特点&#xff1a; 高性能&#xff1a; LSM 树结构使得RocksDB在写入密集型负载下表现…

剪辑视频调色软件有哪些 剪辑视频软件哪个最好 剪辑视频怎么学 剪辑视频的方法和步骤 会声会影2024 会声会影视频制作教程

看了很多调色教程&#xff0c;背了一堆调色参数&#xff0c;可最终还是调不出理想的效果。别再怀疑自己了&#xff0c;不是你的剪辑技术不行&#xff0c;而是剪辑软件没选对。只要掌握了最基本的调色原理&#xff0c;一款适合自己的视频剪辑软件是很容易出片的。 有关剪辑视频…

python实现维特比算法

对于维特比算法&#xff0c;首先想到的就是高通公司&#xff0c;对于现在的通信行业的两大巨头公司之一&#xff0c;高通公司的发家是由器创始人维特比发明了一种高效的通信解码技术&#xff0c;维特比算法。 对于维特比算法是什么&#xff0c;以一个例子来讲述什么是维特比算…