当前位置: 首页 > news >正文

用gin写简单的crud后端API接口

提要

使用gin框架(go的web框架)来创建简单的几个crud接口)
使用技术: gin + sqlite3 + sqlx

创建初始工程

新建文件夹,创建三个子文件夹

分别初始化工程 go mod

如果没有.go文件,执行go mod tidy可能报错(warning: "all" matched no packages), 可以先不弄,只初始化模块就行(go mod init 模块名)
# 项目根目录创建模块
go mod init go_manager 
go mod tidy
# 进入db目录
cd db
# 初始化模块
go mod init go_manager_db 
go mod tidy
# 进入utils目录
cd ../utils
# 初始化模块
go mod init go_manager_utils 
go mod tidy
# 进入web目录
cd ../web
# 初始化模块
go mod init go_manager_web 
go mod tidy

go_manager_db模块编写

创建数据库连接(sqlite如果没有库会自动建)

// db\main.go
package go_manager_dbimport ("fmt""github.com/jmoiron/sqlx"_ "github.com/mattn/go-sqlite3"
)// 数据库相关操作
var db *sqlx.DB// 初始化数据库连接
func InitDB() (err error) {dsn := "./manager.db"// 连接// Open可能仅校验参数,而没有与db间创建连接,// 要确认db是否可用,需要调用Ping。Connect则相当于Open+Ping。db, err = sqlx.Connect("sqlite3", dsn)if err != nil {fmt.Printf("connect DB failed, err:%v\n", err)return}// 最大连接数db.SetMaxOpenConns(100)// 最大空闲连接数db.SetMaxIdleConns(16)// 初始化方法,建表+插入原始数据CreateRoleTable()CreateUserTable()return
}

添加建表方法(初始化权限表和用户表)

// db\main.go
package go_manager_dbimport ("fmt""github.com/jmoiron/sqlx"_ "github.com/mattn/go-sqlite3"
)// 数据库相关操作
var db *sqlx.DB// 初始化数据库连接
func InitDB() (err error) {......}
// 创建用户表
func CreateUserTable() error {sqlc := `CREATE TABLE IF NOT EXISTS "mal_user" (-- sqlite 不能用 comment 添加注释"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT , -- '主键'"uname" varchar(20) NOT NULL UNIQUE , -- '用户昵称'"upass" varchar(50) NOT NULL, -- '密码(md5加密)'"rid" INTEGER NOT NULL UNIQUE DEFAULT 1 -- '角色id'); `_, err := db.Exec(sqlc)if err != nil {fmt.Println(err)return err}// 初始化表//因为有unique约束,所以不会重复添加// sqlStr := "insert into mal_user(uname,upass,rid) values(?,?,?)"Insert("mal_user", []string{"uname", "upass", "rid"}, "admin", "e120012d113ff6ea124a2493453c6dd5", 2)return nil
}// 创建权限表
func CreateRoleTable() error {sqlc := `CREATE TABLE IF NOT EXISTS "mal_role" ("id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, -- '主键' "role" varchar(20) NOT NULL UNIQUE DEFAULT 'user' -- '角色(权限)' ); `_, err := db.Exec(sqlc)if err != nil {return err}// 初始化表// 因为有unique约束,所以不会重复添加 // 有四种权限,id(自增)越大代表权限越大,root>super>admin>userInsert("mal_role", []string{"role"}, "user")Insert("mal_role", []string{"role"}, "admin")Insert("mal_role", []string{"role"}, "super")Insert("mal_role", []string{"role"}, "root")return nil
}

base.go: 通用插入和删除方法

// db\base.go
package go_manager_dbimport ("fmt"_ "github.com/mattn/go-sqlite3"utils "go_manager_utils"
)// 插入数据
func Insert(tableName string, params []string, datas ...interface{}) (err error) {// 拼接 表名(参数1,参数2,...)paramStr := utils.ParamsStr(params)// 拼接 values(?,?,...)values := utils.ValueStr(len(params))var sqlStr = "insert into " + tableName + paramStr + " values" + valuesfmt.Println(sqlStr)_, err = db.Exec(sqlStr, datas...) // 要用...展开if err != nil {fmt.Println(err)fmt.Println("插入数据失败")return}return
}// 删除数据
func Delete(tableName string, id int64) (err error) {sqlStr := "delete from " + tableName + " where id=?"fmt.Println(sqlStr)_, err = db.Exec(sqlStr, id)if err != nil {fmt.Println("删除数据失败")return}return
} 

model.go: 定义数据表对应的结构体

package go_manager_db// 专门定义与数据库交互的结构体// 用户表
type MalUser struct {Id    int64  `db:"id" json:"Rd"`Uname string `db:"uname" json:"Uname"`Upass string `db:"upass" json:"Upass"`Rid   int64  `db:"rid" json:"Rid"`
}
// 角色表
type MalRole struct {Id   int64  `db:"id" json:"Id"`Role string `db:"role" json:"Role"`
}

mal_user.go和mal_role.go: 定义用户表和角色表的crud方法

mal_user.go

package go_manager_dbimport ("fmt" utils "go_manager_utils"_ "github.com/mattn/go-sqlite3"
)// 查数据
func GetAllUser() (users []*MalUser, err error) {sqlStr := `select * from mal_user`// 查询,记录到booklisterr = db.Select(&users, sqlStr)if err != nil {fmt.Println("查询信息失败")fmt.Println(err)return}return
}// 根据id查数据
func GetUserById(id int64) (user MalUser, err error) {// 如果返回的是指针,需要初始化//book=&Book{}sqlStr := "select * from mal_user where id=?"err = db.Get(&user, sqlStr, id)if err != nil {fmt.Println("查询信息失败")return}return
}// 根据name查数据
func GetUserByName(uname string, upass string) (user MalUser, err error) {sqlStr := "select * from mal_user where uname=? and upass=?"err = db.Get(&user, sqlStr, uname, upass)if err != nil {fmt.Println("查询信息失败")return}return
}// 根据id改
func UptUserById(uid string, params []string, datas ...interface{}) (err error) {// 拼接参数列表 xxx=?,xxx=?paramsStr := utils.UptParamsStr(params)// uid直接传字符串拼接sqlStr := "update mal_role set " + paramsStr + " where id=" + uid_, err = db.Exec(sqlStr, datas...)if err != nil {fmt.Println("修改信息失败")return}return
}

mal_role.go

package go_manager_dbimport ("fmt"_ "github.com/mattn/go-sqlite3"
)// 应该id越大,权限越高,比较方便区分权限
// user < admin < super < root
// 查数据
func GetAllRole() (roles []*MalRole, err error) {sqlStr := `select * from mal_role`// 查询,记录到booklisterr = db.Select(&roles, sqlStr)if err != nil {fmt.Println("查询信息失败")fmt.Println(err)return}return
}// 根据id查数据
func GetRoleById(id int64) (role MalRole, err error) {// 如果返回的是指针,需要初始化//book=&Book{}sqlStr := "select * from mal_role where id=?"err = db.Get(&role, sqlStr, id)if err != nil {fmt.Println("查询信息失败")return}return
}// 根据id改数据
func UptRoleById(id int64, roleName string) (err error) {// 如果返回的是指针,需要初始化//book=&Book{}sqlStr := "update mal_role set role=? where id=?"_, err = db.Exec(sqlStr, roleName, id)if err != nil {fmt.Println("修改信息失败")return}return
}

引入项目里的其他模块: utils

在go.mod末尾添加

replace go_manager_utils => ../utils

运行 go mod tidy

go_manager_utils模块编写

jwt.go: 编写加密方法,定时销毁token方法

package go_manager_utilimport ("crypto/md5""fmt""gopkg.in/square/go-jose.v2""gopkg.in/square/go-jose.v2/jwt""time"
)// sign 签名
// 传入密码,加密
func SignJWT(secret string, uname string, upass string) (jwtStr string) {key := []byte(secret)fmt.Println(secret)sig, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.HS256, Key: key},(&jose.SignerOptions{}).WithType("JWT"))if err != nil {panic(err)}cl := jwt.Claims{// Registered claims : 这里有一组预定义的声明,它们不是强制的,但是推荐// 比如:iss (issuer), exp (expiration time), sub (subject), aud (audience)等。Issuer:    uname,Subject:   upass,NotBefore: jwt.NewNumericDate(time.Now()),Audience:  jwt.Audience{"name", "admin"},}raw, err := jwt.Signed(sig).Claims(cl).CompactSerialize()if err != nil {panic(err)}// fmt.Println(raw)return raw
}// 解析jwt
// 传入key(之前加密的密码),raw(jwt令牌)
func ParseJWT(key string, raw string) {var sharedKey = []byte(key)tok, err := jwt.ParseSigned(raw)if err != nil {panic(err)}out := jwt.Claims{}// 解析出issuer(uname)和subject(upass),校验if err := tok.Claims(sharedKey, &out); err != nil {panic(err)}fmt.Printf("iss: %s, sub: %s\n", out.Issuer, out.Subject)
}// DM5加密
func MD5(str string) string {data := []byte(str) //切片has := md5.Sum(data)md5str := fmt.Sprintf("%x", has) //将[]byte转成16进制return md5str
}// 销毁TokenMap的方法
// 定时销毁token(默认2小时)
func DestoryTokenMap(tokenMap map[string]string) {for k := range tokenMap {delete(tokenMap, k)}
}

myTime.go: 定义定时器方法

package go_manager_utilimport ( "time"
)// 定义函数类型
type Fn func() error// 定时器中的成员
type MyTicker struct {MyTick *time.TickerRunner Fn
}
type MyTimer struct {MyTime *time.TimerRunner Fn
}func NewMyTick(interval int, f Fn) *MyTicker {return &MyTicker{MyTick: time.NewTicker(time.Duration(interval) * time.Second),Runner: f,}
}// 一次性
func NewMyTimer(interval int, f Fn) *MyTimer {return &MyTimer{MyTime: time.NewTimer(time.Duration(interval) * time.Second),Runner: f,}
}// 启动定时器需要执行的任务
func (t *MyTicker) Start() {for {select {case <-t.MyTick.C:t.Runner()}}
}// 启动定时器需要执行的任务
func (t *MyTimer) Start() { select {case <-t.MyTime.C:t.Runner()} 
}// func over() error {
//     fmt.Println("token过期")
//     return nil
// }
// 测试
// func main() {
//     t := NewMyTimer(2, over)
//     t.Start()
// }

res.go: 响应前端请求的方法

package go_manager_utilimport ("fmt""github.com/gin-gonic/gin""net/http"
)/* 通用响应方法 */
func R(c *gin.Context, err error, msg interface{}, data interface{}) {// 如果有err,就说明是有错误,就返回错误响应(msg)if err != nil {fmt.Println(err)c.JSON(http.StatusInternalServerError, gin.H{"status": 500,"msg":    msg,})return}// 返回正确响应(data)c.JSON(http.StatusOK, gin.H{"status": 200,"msg":    data,})
}

stringUtils.go: 封装字符串操作方法

package go_manager_utils// 拼接sql语句的value
// len是语句有几个参数
func ValueStr(len int) (values string) {// 拼接 values(?,?,...)values = "("for i := 0; i < len-1; i++ {values += "?"values += ","}values += "?"values += ")"return
}// 拼接sql语句update的param
// params是参数名数组
func UptParamsStr(params []string) (paramStr string) {// 拼接参数列表 xxx=?,xxx=?paramStr = ""for i := 0; i < len(params)-1; i++ {paramStr += params[i]paramStr += "=?,"}paramStr += params[len(params)-1]paramStr += "=?"return
}// 拼接sql语句的param
// params是参数名数组
func ParamsStr(params []string) (paramStr string) {// 拼接 表名(参数1,参数2,...)paramStr = "("for i := 0; i < len(params)-1; i++ {paramStr += params[i]paramStr += ","}paramStr += params[len(params)-1]paramStr += ")"return
}

运行go mod tidy处理go文件里的依赖

go_manager_web模块编写

main.go: 主要逻辑,创建web实例,注册路由...

package go_manager_webimport ("fmt""github.com/gin-gonic/gin"db "go_manager_db"utils "go_manager_utils""net/http"
)// 定义路由组
// 组中组(嵌套路由组)
func DefineRouteGroup(fatherGroup *gin.RouterGroup, groupName string, r *gin.Engine) *gin.RouterGroup {var group *gin.RouterGroupif fatherGroup != nil {// v1/groupNamegroup = fatherGroup.Group(groupName)} else {// /groupNamegroup = r.Group(groupName)}// 返回路由组return group
}// 存放 token (不同ip不同token)
var TokenMap = make(map[string]string, 10)// 定时销毁token
func timeDT() {// 两小时后销毁t := utils.NewMyTimer(2*60*60, func() error {utils.DestoryTokenMap(TokenMap)return nil})t.Start()fmt.Println(TokenMap)
}// 路由和处理函数放在不同文件好像会使中间件失效
func Login(c *gin.Context) { user := db.MalUser{}// 绑定json和结构体(接收json,数据放入结构体)if err := c.BindJSON(&user); err != nil {return}uname := user.Unameupass := user.Upass userModel, err := db.GetUserByName(uname, upass)if err != nil || &userModel == nil {fmt.Println(err)c.JSON(500, gin.H{"status": 500,"msg":    "登录失败",})return} token := utils.SignJWT("malred", uname, upass)// 存入map// fmt.Println(c.ClientIP(),c.RemoteIP())TokenMap[c.ClientIP()] = tokenfmt.Println(TokenMap)c.JSON(http.StatusOK, gin.H{"status": 200,"msg":    "登录成功",// 返回jwt令牌(密码因为前端md5加密过,所以直接放入jwt)"token": token,})go timeDT()
}// 路由器
// 启动默认的路由
var r = gin.Default()// user路由组
var v1 *gin.RouterGroupfunc Run() {// 使用中间件// 日志r.Use(gin.Logger())// 错误恢复r.Use(gin.Recovery())// 跨域r.Use(Core())// 阻止缓存响应r.Use(NoCache())// 安全设置r.Use(Secure())// 创建路由组v1v1 = DefineRouteGroup(nil, "v1", r)v1.POST("login", Login)// 注册user的路由registerUser(Token(), Core())// 注册role的路由registerRole(Token(), Core())// 启动webserver,监听本地127.0.0.1(默认)端口r.Run(":10101")
}

moddilewares.go: 中间件

package go_manager_webimport ( utils "go_manager_utils""net/http""strconv""time""github.com/gin-gonic/gin"
)//解决跨域问题
func Core() gin.HandlerFunc {return func(c *gin.Context) {method := c.Request.Methodc.Header("Access-Control-Allow-Origin", "*")c.Header("Access-Control-Allow-Headers", "*")c.Header("Access-Control-Allow-Methods", "*")c.Header("Access-Control-Expose-Headers", "Content-Length,Access-Control-Allow-Origin,Access-Control-Allow-Headers,Content-Type")c.Header("Access-Control-Max-Age", "3600")c.Header("Access-Control-Allow-Credentials", "true")//放行索引optionsif method == "OPTIONS" {c.AbortWithStatus(http.StatusNoContent)}//处理请求c.Next()}
}// 权限认证(验证token)
func Token() gin.HandlerFunc {return func(c *gin.Context) {// for k, v := range c.Request.Header {//     fmt.Println(k, v)// }secret := c.Request.Header["Secret"] // 获取前端传来的secrettoken := c.Request.Header["Token"]if len(token) == 0 {// 验证不通过,不再调用后续的函数处理c.Abort()c.JSON(http.StatusUnauthorized, gin.H{"code":    401,"message": "访问未授权",})return}timeInt64 := strconv.FormatInt(time.Now().UnixNano()/1e6/1000/60, 10)md5Str := utils.MD5(timeInt64 + TokenMap[c.ClientIP()])// fmt.Println(TokenMap[c.ClientIP()], timeInt64)// fmt.Println(timeInt64 + TokenMap[c.ClientIP()])// fmt.Println(md5Str, secret[0])if md5Str != secret[0] {// 验证不通过,不再调用后续的函数处理c.Abort()c.JSON(http.StatusUnauthorized, gin.H{"code":    401,"message": "访问未授权",})return}// 验证jwt// utils.ParseJWT(secret[0][8:11]+secret[0][19:22], token[0])//处理请求c.Next()}
}// 阻止缓存响应
func NoCache() gin.HandlerFunc {return func(ctx *gin.Context) {ctx.Header("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate, value")ctx.Header("Expires", "Thu, 01 Jan 1970 00:00:00 GMT")ctx.Header("Last-Modified", time.Now().UTC().Format(http.TimeFormat))ctx.Next()}
}// 响应 options 请求, 并退出
// func Options() gin.HandlerFunc {
//     return func(ctx *gin.Context) {
//         if ctx.Request.Method != "OPTIONS" {
//             ctx.Next()
//         } else {
//             ctx.Header("Access-Control-Allow-Origin", "*")
//             ctx.Header("Access-Control-Allow-Methods", "GET,POST,PUT,PATCH,DELETE,OPTIONS")
//             ctx.Header("Access-Control-Allow-Headers", "authorization, origin, content-type, accept")
//             ctx.Header("Allow", "HEAD,GET,POST,PUT,PATCH,DELETE,OPTIONS")
//             ctx.Header("Content-Type", "application/json")
//             ctx.AbortWithStatus(200)
//         }
//     }
// }// 安全设置
func Secure() gin.HandlerFunc {return func(ctx *gin.Context) {ctx.Header("Access-Control-Allow-Origin", "*")ctx.Header("X-Frame-Options", "DENY")ctx.Header("X-Content-Type-Options", "nosniff")ctx.Header("X-XSS-Protection", "1; mode=block")if ctx.Request.TLS != nil {ctx.Header("Strict-Transport-Security", "max-age=31536000")}// Also consider adding Content-Security-Policy headers// ctx.Header("Content-Security-Policy", "script-src 'self' https://cdnjs.cloudflare.com")}
}// todo 权限控制(token携带当前用户的权限信息,过滤低于指定权限的请求)

role.go和user.go: 真的role和user表的web操作

role.go

package go_manager_webimport ("fmt"db "go_manager_db"utils "go_manager_utils""strconv" "github.com/gin-gonic/gin"
)func GetAllRoleHandler(c *gin.Context) {roles, err := db.GetAllRole()// 通用响应utils.R(c, err, "获取角色列表失败", roles)
}
func AddRoleHandler(c *gin.Context) {// Role := c.PostForm("Role")// fmt.Println(Role)role := db.MalRole{}//绑定json和结构体if err := c.BindJSON(&role); err != nil {return}Role := role.Roleerr := db.Insert("mal_role", []string{"role"}, Role)// 通用响应utils.R(c, err, "添加角色失败", "添加角色成功")
}
func DelRoleHandler(c *gin.Context) {// 从url获取参数idStr := c.Query("rid")// fmt.Println(idStr)rid, err := strconv.ParseInt(idStr, 10, 64)err = db.Delete("mal_role", rid)// 通用响应utils.R(c, err, "删除角色失败", "删除角色成功")
}
func GetOneRoleHandler(c *gin.Context) {// 从url获取参数idStr := c.Query("rid")fmt.Println(idStr)rid, _ := strconv.ParseInt(idStr, 10, 64)one, err2 := db.GetRoleById(rid)// 通用响应utils.R(c, err2, "查询角色失败", one)
}
func UptRoleHandler(c *gin.Context) {role := db.MalRole{}//绑定json和结构体if err := c.BindJSON(&role); err != nil {return} rid := role.IdroleName := role.Rolefmt.Println(role)err := db.UptRoleById(rid, roleName)// 通用响应utils.R(c, err, "修改角色失败", "修改角色成功")
}
func registerRole(middles ...gin.HandlerFunc) {// 创建路由组v1/userrole := DefineRouteGroup(v1, "role", r)// 添加中间件if middles != nil {role.Use(middles...)}// 获取所有role.GET("all", GetAllRoleHandler)// 添加role.POST("add", AddRoleHandler)// 删除role.DELETE("del", DelRoleHandler)// 根据id获取role.GET("id", GetOneRoleHandler)// 根据id修改role.PUT("upt", UptRoleHandler)
}

user.go

package go_manager_webimport ("fmt"db "go_manager_db"utils "go_manager_utils""strconv" "github.com/gin-gonic/gin"
)func GetAllUserHandler(c *gin.Context) {users, err := db.GetAllUser()// 通用响应utils.R(c, err, "查询角色失败", users)
}
func AddUserHandler(c *gin.Context) {// uname := c.PostForm("uname")// upass := c.PostForm("upass")// idStr := c.PostForm("rid")user := db.MalUser{}//绑定json和结构体if err := c.BindJSON(&user); err != nil {return}uname := user.Unameupass := user.Upassrid := user.Ridfmt.Println(user)// rid, err := strconv.ParseInt(idStr, 10, 64)err := db.Insert("mal_user", []string{"uname", "upass", "rid"}, uname, upass, rid)// 通用响应utils.R(c, err, "添加角色失败", "添加角色成功")
}
func DelUserHandler(c *gin.Context) {// 从url获取参数idStr := c.Query("uid")// fmt.Println(idStr)uid, err := strconv.ParseInt(idStr, 10, 64)err = db.Delete("mal_user", uid)// 通用响应utils.R(c, err, "删除角色失败", "删除角色成功")
}
func GetOneUserHandler(c *gin.Context) {// 从url获取参数idStr := c.Query("uid")fmt.Println(idStr)uid, _ := strconv.ParseInt(idStr, 10, 64)one, err2 := db.GetUserById(uid)// 通用响应utils.R(c, err2, "查询角色失败", one)
}
func UptUserHandler(c *gin.Context) {// 从url获取参数// uid := c.PostForm("uid")// uname := c.PostForm("uname")// upass := c.PostForm("upass")// ridStr := c.PostForm("rid")user := db.MalUser{}//绑定json和结构体if err := c.BindJSON(&user); err != nil {return}uname := user.Unameupass := user.Upassrid := user.Riduid := user.Id// fmt.Println(idStr, UserName)// rid, _ := strconv.ParseInt(ridStr, 10, 64)err := db.UptUserById(strconv.FormatInt(uid, 10), []string{"uname", "upass", "rid"}, uname, upass, rid)// 通用响应utils.R(c, err, "修改角色失败", "修改角色成功")
}
func registerUser(middles ...gin.HandlerFunc) {// 创建路由组v1/useruser := DefineRouteGroup(v1, "user", r)// 添加中间件if middles != nil {user.Use(middles...)}user.GET("all", GetAllUserHandler)// 添加user.POST("add", AddUserHandler)// 删除user.DELETE("del", DelUserHandler)// 根据id获取user.GET("id", GetOneUserHandler)// 根据id修改user.PUT("upt", UptUserHandler)
}

运行go mod tidy

忘了,要引用项目里的其他包

replace go_manager_utils => ../utils
replace go_manager_db => ../db

go mod tidy

编写根目录的go_manager模块

main.go

package mainimport ( db "go_manager_db"web "go_manager_web"
)func main() {// 初始化数据库db.InitDB()   // 开启服务web.Run()
}

go.mod

module go_managergo 1.18replace go_manager_web => ./webreplace go_manager_db => ./dbreplace go_manager_utils => ./utils

go mod tidy

测试(可以用go build打包)

完整目录结构

go run main.go

因为后端存的密码是md5加密过的,所以前端也要传md5加密的密码,二者相同才能通过

安全: 我的安全不咋地,加密的方法是前端根据当前时间戳(转为分钟,防止因为前后端延迟而导致时间戳不一致)+登录后从后端获取的token来md5,每次请求都会验证这个md5(后端也加密(时间戳/60+token)然后对比),这个就不测试了

代码仓库:

https://gitee.com/malguy/go-manager

配套前端管理系统(react18):

https://github.com/malred/base-manager

相关文章:

用gin写简单的crud后端API接口

提要使用gin框架(go的web框架)来创建简单的几个crud接口)使用技术: gin sqlite3 sqlx创建初始工程新建文件夹,创建三个子文件夹分别初始化工程 go mod如果没有.go文件,执行go mod tidy可能报错(warning: "all" matched no packages), 可以先不弄,只初始化模块就行(…...

CF大陆斗C战士(三)

文章目录[C. Good Subarrays](https://codeforces.com/problemset/problem/1398/C)题目大意题目分析code[C. Boboniu and Bit Operations](https://codeforces.com/problemset/problem/1395/C)题目大意题目分析code[C. Rings](https://codeforces.com/problemset/problem/1562/…...

TTS | 语音合成论文概述

综述系列2021_A Survey on Neural Speech Synthesis论文&#xff1a;2106.15561.pdf (arxiv.org)论文从两个方面对神经语音合成领域的发展现状进行了梳理总结&#xff08;逻辑框架如图1所示&#xff09;&#xff1a;核心模块&#xff1a;分别从文本分析&#xff08;textanalysi…...

HTML第5天 HTML新标签与特性

新标签与特性文档类型设定前端复习帮手W3Schoool常用新标签datalist标签&#xff0c;与input元素配合&#xff0c;定义选项列表fieldset元素新增input表单文档类型设定 document – HTML: 开发环境输入html:4s – XHTML: 开发环境输入html:xt – HTML5: 开发环境输入html:5 前…...

java ee 之进程

目录 1.进程的概念 2.进程管理 3.进程属性(pcb) 3.1pid 3.2内存指针 3.3文件描述符 3.4进程调度 3.4.1进程状态 3.4.2 进程的优先级 3.4.3进程的上下文 3.4.4进程的记账信息 5.进程间通信 1.进程的概念 一个运行起来的程序,就是进程 .exe是一个可执行文件(程序),双…...

Linux学习记录——십사 进程控制(1)

文章目录1、进程创建1、fork函数2、进程终止1、情况分类2、如何理解进程终止3、进程终止的方式3、进程等待1、进程创建 1、fork函数 fork函数从已存在进程中创建一个新进程&#xff0c;新进程为子进程&#xff0c;原进程为父进程。 #include <unistd.h> pid_t fork(vo…...

使用 create-react-app 脚手架搭建React项目

❀官网 1、安装脚手架&#xff1a;npm install -g create-react-app 2、查看版本&#xff1a;create-react-app -V &#xff01;&#xff01;&#xff01;注意 Node版本必须是14以上&#xff0c;不然会报以下错误。 3、创建react项目&#xff08;项目名不能包含大写字母&…...

inquirerjs

inquirerjs inquirerjs是一个用来实现命令行交互界面的工具集合。它帮助我们实现与用户的交互交流&#xff0c;比如给用户一个提醒&#xff0c;用户给我们一个答案&#xff0c;我们根据用户的答案来做一些事情&#xff0c;典型应用如plop等生成器工具。 npm install inquirer…...

[数据库]内置函数

●&#x1f9d1;个人主页:你帅你先说. ●&#x1f4c3;欢迎点赞&#x1f44d;关注&#x1f4a1;收藏&#x1f496; ●&#x1f4d6;既选择了远方&#xff0c;便只顾风雨兼程。 ●&#x1f91f;欢迎大家有问题随时私信我&#xff01; ●&#x1f9d0;版权&#xff1a;本文由[你帅…...

shell基本知识

为什么学习和使用Shell编程 什么是Shell shell的起源 shell的功能 shell的分类 如何查看当前系统支持的shell&#xff1f; 如何查看当前系统默认shell&#xff1f; 驼峰语句 shell脚本的基本元素 shell脚本编写规范 shell脚本的执行方式 shell脚本的退出状态 &#xf…...

Http长连接和短连接

http1.0以前&#xff0c;默认使用的是短连接&#xff0c;客户端与服务器之间每进行一次http操作&#xff0c;就会建立一次连接&#xff0c;例如&#xff0c;打开一个网页&#xff0c;包括html文件&#xff0c;js&#xff0c;css&#xff0c;每获取一次资源&#xff0c;就需要进…...

[SQL Statements] 基本的SQL知识 之DDL针对表结构和表空间的基本操作

[SQL Statements] 基本的SQL知识 之DDL针对表结构和表空间的基本操作 什么是数据库的表以及表空间 在MySQL中&#xff0c;一个数据库可以包含多个表&#xff0c;每个表是由若干个列&#xff08;column&#xff09;和行&#xff08;row&#xff09;组成的。表是存储数据的基本…...

Git版本控制工具(详解)

Git版本控制工具 Git常见命令速查表 集中式版本控制 cvs和svn都是属于集中式版本控制系统 他们的主要特点是单一的集中管理服务器 保存所有文件的修订版本协同开发人员通过客户端连接到这台服务器 取出最新的文件或者提交更新 优点每个人都可以在一定程度上看到项目中的其他…...

408考研计算机之计算机组成与设计——知识点及其做题经验篇目2:指令系统

今天我们来讲一讲指令系统里面的知识点以及做题技巧 1、定义 考点1&#xff1a;指令定义 指令是指示计算机执行某种操作的命令&#xff0c;一台计算机的所有指令的集合构成该机的指令系统&#xff0c;也称为指令集。指令系统是指令集体系结构ISA中最核心的部分&#xff0c;ISA…...

Java语法中的方法引用::是个什么鬼?

1.函数式接口 函数式接口&#xff08;Functional Interface&#xff09;就是一个有且仅有一个抽象方法&#xff08;通俗来说就是只有一个方法要去被实现&#xff0c;因此我们也能通过这个去动态推断参数类型&#xff09;&#xff0c;但是可以拥有多个非抽象方法的接口。函数式接…...

【使用vue init和vue create的区别以及搭建vue项目的教程】

vue init 是vue-cli2.x的初始化方式&#xff0c;可以使用github上面的一些模板来初始化项目 webpack是官方推荐的标准模板名 使用方式&#xff1a;vue init webpack 项目名称 例如使用github上面electron-vue的模板使用方式&#xff1a;vue init electron-vue 项目名称教程目…...

二、HTTP协议02

文章目录一、HTTP状态管理Cookie和Session二、HTTP协议之身份认证三、HTTP长连接与短连接四、HTTP中介之代理五、HTTP中介之网关六、HTTP之内容协商七、断点续传和多线程下载一、HTTP状态管理Cookie和Session HTTP的缺陷无状态。Cookie和Session就用来弥补这个缺陷的。 Cooki…...

免费Api接口汇总(亲测可用,可写项目)

免费Api接口汇总&#xff08;亲测可用&#xff09;1. 聚合数据2. 用友API3. 天行数据4. Free Api5. 购物商城6. 网易云音乐API7. 疫情API8. 免费Api合集1. 聚合数据 https://www.juhe.cn/ 2. 用友API http://iwenwiki.com/wapicovid19/ 3. 天行数据 https://www.tianapi.com…...

12.并发编程

1.并发并发&#xff1a;逻辑流在时间时重叠构造并发程序&#xff1a;进程&#xff1a;每个逻辑控制流是一个进程&#xff0c;由内核调度和维护进程有独立的虚拟地址空间&#xff0c;想要通信&#xff0c;控制流必须使用某种显式的进程间通信机制(IPC)I/O多路复用&#xff1a;程…...

C/C++指针与数组(一)

预备知识 1、数据的存储 2、基本内建类型 1&#xff09;类型的大小 C offers a flexible standard with some guaranteed minimum sizes, which it takes from C: A short integer is at least 16 bits wide.An int integer is at least as big as short.A long integer is a…...

Android使用移动智能终端补充设备标识获取OAID

官网http://www.msa-alliance.cn/col.jsp?id120首先到官网注册账号&#xff0c;申请下载相关sdk和授权证书2.把 oaid_sdk_x.x.x.aar 拷贝到项目的 libs 目录&#xff0c;并设置依赖&#xff0c;其中x.x.x 代表版本号3.supplierconfig.json 拷贝到项目 assets 目录下&#xff0…...

极目智能与锐算科技达成战略合作,4D毫米波成像雷达助力智能驾驶落地

近日&#xff0c;智能驾驶方案提供商武汉极目智能技术有限公司&#xff08;以下简称“极目智能”&#xff09;宣布与毫米波成像雷达公司锐算&#xff08;上海&#xff09;科技有限公司&#xff08;以下简称“锐算科技”&#xff09;达成战略合作&#xff0c;双方将合作开发基于…...

OpenCV基础(一)

1.认识图像&#xff08;彩色图中每一个像素点都包含三个颜色通道RGB&#xff0c;数值范围为0~255&#xff0c;0代表黑色&#xff0c;255代表白色&#xff09; import cv2 #opencv 读取的格式为BGRimg cv2.imread(cat.png) #读取图像 cv2.imshow(cat, img) #显示图像img&#x…...

pinia 的使用(笔记)

文章目录1. Pinia 与 Vuex 的区别2. pinia 安装与搭建3. pinia 的使用3.1 基本使用3.2 订阅状态3.3 订阅 actions1. Pinia 与 Vuex 的区别 Pinia 是 Vue 的状态管理库&#xff0c;相当于 Vuex 取消了 mutations&#xff0c;取消了 Module 模块化命名空间现在的 pinia 采用的是…...

DolphinDB 机器学习在物联网行业的应用:实时数据异常率预警

数据异常率预警在工业安全生产中是一项重要工作&#xff0c;对于监控生产过程的稳定性&#xff0c;保障生产数据的有效性&#xff0c;维护生产设备的可靠性具有重要意义。随着大数据技术在生产领域的深入应用&#xff0c;基于机器学习的智能预警已经成为各大生产企业进行生产数…...

新建vite+vue3+ts项目,以及解决过程中遇到的问题

目录 一、新建vitevue3ts项目 二、解决过程中遇到的问题 解决报错&#xff1a;Module ‘“xx.vue“‘ has no default export. 解决报错&#xff1a;Error [ERR_MODULE_NOT_FOUND]: Cannot find package ‘uuid’ imported from xxx的解决 解决报错&#xff1a;[plugin:vi…...

pyppeteer中文文档

目录 1.命令 ​​​​​​2.环境变量 3.Launcher&#xff08;启动器&#xff09; 4.浏览器类 5.浏览器上下文类 6.页面类 7.Worker 类 8.键盘类 9.鼠标类 10.Tracing类 11.对话框类 12.控制台消息类 13.Frame 类 14.执行上下文类 15.JSHandle 类 16.元素句柄类…...

(二十四)操作系统-吸烟者问题

文章目录一、问题描述二、问题分析1&#xff0e;关系分析2&#xff0e;整理思路3&#xff0e;设置信号量三、实现四、总结一、问题描述 假设一个系统有三个抽烟者进程和一个供应者进程。每个抽烟者不停地卷烟并抽掉它&#xff0c;但是要卷起并抽掉一支烟&#xff0c;抽烟者需要…...

ReentranLock(可重入锁)

一、ReentranLock ReentranLock属于JUC并发工具包下的类&#xff0c;相当于 synchronized具备如下特点 ● 可中断 ● 可以设置超时时间 ● 可以设置为公平锁&#xff08;防止线程出现饥饿的情况&#xff09; ● 支持多个条件变量 与 synchronized一样&#xff0c;都支持可重…...

Kafka 入门 (一)

Kafka 入门&#xff08;一&#xff09; Apache Kafka起源于LinkedIn&#xff0c;后来于2011年成为开源Apache项目&#xff0c;然后于2012年成为First-class Apache项目。Kafka是用Scala和Java编写的。 Apache Kafka是基于发布订阅的容错消息系统。 它是快速&#xff0c;可扩展…...