Go Web 开发 Demo【用户登录、注册、验证】

前言

        这篇文章主要是学习怎么用 Go 语言(Gin)开发Web程序,前端太弱了,得好好补补课,完了再来更新。

1、环境准备

新建项目,生成 go.mod 文件:

出现报错:go: modules disabled by GO111MODULE=off; see 'go help modules',说明需要开启:

临时设置环境变量: 

set GO111MODULE=on # windows
export GO111MODULE=on # linux

 永久设置环境变量:

再次生成 go.mod 文件:

执行完毕,发现项目下生成 go.mod 文件:

这里的模块名称是我们自定义的,不是说非得和哪个目录或者项目名对应上!

2、用户注册

2.1、需求

  • 用户向地址 /register 发送POST请求(表单携带着 username、password、phone)
  • 后端处理请求(检查手机号位数、手机号是否已经被注册、用户名是否为空)

2.2、需求实现

2.2.1、判断手机号是否存在

func isPhoneExist(db gorm.DB, phone string) bool {user := User{}db.Where("phone = ?", phone).First(user)if user.ID != 0 {return true}return false
}

2.2.2、生成随机10位的用户名

func RandomString(length int) string {var letters = []byte("qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM")result := make([]byte, length)rand.Seed(time.Now().Unix())for i := range result {result[i] = letters[rand.Intn(len(letters))]}return string(result)
}

2.2.3、设置用户表结构体

        这个结构体的字段会对应用户表中的每个字段,我们会在初始化数据库的时候,使用 gorm.DB 的  AutoMigrate 方法自动帮我进行创建这个结构体对应的表。

type User struct {gorm.ModelName     string `gorm:"type:varchar(20);not null"`Phone    string `gorm:"type:varchar(110);not null,unique"`Password string `gorm:"size:255;not null"`
}

 其中 gorm.Model 的源码如下:

我们通过嵌套 gorm.Model 来给我们的表增加四个字段。

2.2.4、获得数据库连接(gorm)

func InitDB() *gorm.DB {driverName := "mysql"host := "127.0.0.1"port := 3306database := "go_web"username := "root"password := "xxxxx"charset := "utf8mb4"args := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=true", username, password, host, port, database, charset)db, err := gorm.Open(driverName, args)if err != nil {panic("failed to connect database, err : " + err.Error())}db.AutoMigrate(&User{}) // 如果表不存在则自动创建return db
}

2.3、完整代码

package mainimport ("fmt""github.com/gin-gonic/gin"_ "github.com/go-sql-driver/mysql""github.com/jinzhu/gorm""math/rand""net/http""time"
)type User struct {gorm.ModelName     string `gorm:"type:varchar(20);not null"`Phone    string `gorm:"type:varchar(110);not null,unique"`Password string `gorm:"size:255;not null"`
}func main() {db := InitDB()defer db.Close()engine := gin.Default()engine.POST("/register", func(ctx *gin.Context) {// 获取参数name := ctx.PostForm("username")phone := ctx.PostForm("phone")password := ctx.PostForm("password")// 数据验证if len(phone) != 11 {ctx.JSON(http.StatusUnprocessableEntity, gin.H{"code": 422,"msg":  "手机号必须为11位!",})return}if len(password) < 6 {// gin.H 等同于 map[string]anyctx.JSON(http.StatusUnprocessableEntity, map[string]interface{}{"code": 422,"msg":  "密码不能少于6位!",})return}if len(name) == 0 {name = RandomString(10)}fmt.Println(name, phone, password)// 判断手机号是否存在if isPhoneExist(*db, phone) {ctx.JSON(http.StatusUnprocessableEntity, gin.H{"msg": "用户已存在,不允许重复注册",})return}// 创建用户newUser := User{Name:     name,Phone:    phone,Password: password,}db.Create(&newUser)// 返回结果ctx.JSON(200, gin.H{"msg": "注册成功",})})panic(engine.Run()) // 默认端口 8080
}func isPhoneExist(db gorm.DB, phone string) bool {user := User{}db.Where("phone = ?", phone).First(user)if user.ID != 0 {return true}return false
}func RandomString(length int) string {var letters = []byte("qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM")result := make([]byte, length)rand.Seed(time.Now().Unix())for i := range result {result[i] = letters[rand.Intn(len(letters))]}return string(result)
}func InitDB() *gorm.DB {driverName := "mysql"host := "127.0.0.1"port := 3306database := "go_web"username := "root"password := "xxxxx"charset := "utf8mb4"args := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=true", username, password, host, port, database, charset)db, err := gorm.Open(driverName, args)if err != nil {panic("failed to connect database, err : " + err.Error())}db.AutoMigrate(&User{}) // 如果表不存在则自动创建return db
}

2.4、测试

使用规范的用户信息再次注册:

3、项目重构

        上面我们把连接数据库和响应的代码都放到了一个文件中,显然后期随着业务代码越来越多开发起来越来越难以管理,所以我们这里需要对项目进行重构:

3.1、util 层

存放工具,比如我们上面的随机生成用户名方法

package utilimport ("math/rand""time"
)func RandomString(length int) string {var letters = []byte("qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM")result := make([]byte, length)rand.Seed(time.Now().Unix())for i := range result {result[i] = letters[rand.Intn(len(letters))]}return string(result)
}

3.2、model 层

存放结构体

package modelimport "github.com/jinzhu/gorm"type User struct {gorm.ModelName     string `gorm:"type:varchar(20);not null"`Phone    string `gorm:"type:varchar(110);not null,unique"`Password string `gorm:"size:255;not null"`
}

3.3、common 层

存放一些公共的方法,比如连接数据库工具

package commonimport ("com.lyh/goessential/model""fmt""github.com/jinzhu/gorm"
)var DB *gorm.DBfunc InitDB() *gorm.DB {driverName := "mysql"host := "127.0.0.1"port := 3306database := "go_web"username := "root"password := "Yan1029."charset := "utf8mb4"args := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=true", username, password, host, port, database, charset)db, err := gorm.Open(driverName, args)if err != nil {panic("failed to connect database, err : " + err.Error())}db.AutoMigrate(&model.User{}) // 如果表不存在则自动创建DB = dbreturn db
}func GetDB() *gorm.DB {return DB
}

3.4、controller 层

        存放控制器,因为我们使用的 Gin 框架的请求方法都是函数式编程,它的第二个参数是一个处理请求的函数,所以控制器层我们存放的是业务模块对应的方法:

package controllerimport ("com.lyh/goessential/common""com.lyh/goessential/model""com.lyh/goessential/util""fmt""github.com/gin-gonic/gin""github.com/jinzhu/gorm""net/http"
)func Register(ctx *gin.Context) {DB := common.GetDB()// 获取参数name := ctx.PostForm("username")phone := ctx.PostForm("phone")password := ctx.PostForm("password")// 数据验证if len(phone) != 11 {ctx.JSON(http.StatusUnprocessableEntity, gin.H{"code": 422,"msg":  "手机号必须为11位!",})return}if len(password) < 6 {// gin.H 等同于 map[string]anyctx.JSON(http.StatusUnprocessableEntity, map[string]interface{}{"code": 422,"msg":  "密码不能少于6位!",})return}if len(name) == 0 {name = util.RandomString(10)}fmt.Println(name, phone, password)// 判断手机号是否存在if isPhoneExist(DB, phone) {ctx.JSON(http.StatusUnprocessableEntity, gin.H{"msg": "用户已存在,不允许重复注册",})return}// 创建用户newUser := model.User{Name:     name,Phone:    phone,Password: password,}DB.Create(&newUser)// 返回结果ctx.JSON(200, gin.H{"msg": "注册成功",})
}func isPhoneExist(db *gorm.DB, phone string) bool {user := model.User{}db.Where("phone = ?", phone).First(&user) // 这里的参数是地址if user.ID != 0 {return true}return false
}

注意:这里判断用户是否存在需要传入一个 user 地址,因为 user 是值类型,如果不传入地址,则进入方法后的操作无效。 

3.5、routes.go

        所有的请求都将通过这个文件中的方法再传递给 main 方法,其实就是为了简化 main 方法所在go文件的代码量,方便管理和维护。所以它的包名也是 main,相当于它俩在一个文件内。

package mainimport ("com.lyh/goessential/controller""github.com/gin-gonic/gin"
)func CollectRoute(engine *gin.Engine) *gin.Engine {engine.POST("/register", controller.Register)return engine
}

3.6、main.go

这是程序的入口,现在我们已经把它彻底解脱出来了:

package mainimport ("com.lyh/goessential/common""github.com/gin-gonic/gin"_ "github.com/go-sql-driver/mysql"
)func main() {db := common.InitDB()defer db.Close()engine := gin.Default()engine = CollectRoute(engine)panic(engine.Run()) // 默认端口 8080
}

测试

因为我们有两个文件都是 main 包,所以我们最好使用命令启动:

4、密码加密以及登录测试

4.1、注册加密

        在 controller 的注册方法( Register )中修改创建用户的代码,对将要插入数据库中的代码进行加密:

	// 创建用户hasedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)if err != nil {ctx.JSON(http.StatusInternalServerError, gin.H{"code": 500,"msg":  "加密错误",})return}newUser := model.User{Name:     name,Phone:    phone,Password: string(hasedPassword),}DB.Create(&newUser)

4.2、登录方法

func Login(ctx *gin.Context) {DB := common.GetDB()// 获取参数phone := ctx.PostForm("phone")password := ctx.PostForm("password")// 数据验证if len(phone) != 11 {ctx.JSON(http.StatusUnprocessableEntity, gin.H{"code": 422,"msg":  "手机号必须为11位!",})return}if len(password) < 6 {// gin.H 等同于 map[string]anyctx.JSON(http.StatusUnprocessableEntity, map[string]interface{}{"code": 422,"msg":  "密码不能少于6位!",})return}// 判断手机号是否存在user := model.User{}DB.Where("phone = ?", phone).First(&user)if user.ID == 0 {ctx.JSON(http.StatusUnprocessableEntity, gin.H{"msg": "用户不存在",})return}// 判断密码是否正确if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)); err != nil {ctx.JSON(http.StatusBadRequest, gin.H{"code": 400,"msg":  "密码错误",})return}// 返回 token 给前端token := "11"// 返回结果ctx.JSON(200, gin.H{"code": 200,"data": gin.H{"token": token,},"msg": "登录成功",})}

4.3、注册请求

        把我们的 Login 方法注册到 /login 地址(只需要在 routes.go 文件的 CollectRoute 函数中添加一行即可):

测试

查看数据库:

登录测试

5、jwt 实现用户认证

jwt 地址:github.com/dgrijalva/jwt-go

5.1、发放 token

在 common 包下来创建一个 jwt.go 文件,定义发放 token 的函数:

package commonimport ("com.lyh/goessential/model""github.com/dgrijalva/jwt-go""time"
)var jwtKey = []byte("a_secret_crect")type Claims struct {UserId uintjwt.StandardClaims
}func ReleaseToken(user model.User) (string, error) {expirationTime := time.Now().Add(7 * 24 * time.Hour) // 设置token有效期7天claims := &Claims{UserId: user.ID,StandardClaims: jwt.StandardClaims{ExpiresAt: expirationTime.Unix(), // 过期时间IssuedAt:  time.Now().Unix(),     // 发放的时间Issuer:    "lyh",                 // 发送者Subject:   "user token",          // 发送主题},}token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)tokenString, err := token.SignedString(jwtKey)if err != nil {return "", err}return tokenString, nil
}// 从 tokenString 中解析出 claims 然后返回
func ParseToken(tokenString string) (*jwt.Token, *Claims, error) {claims := &Claims{}token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {return jwtKey, nil})return token, claims, err
}

5.2、设置返回 token

        在之前 controller 层下用户模块中的登录请求(/login)中设置返回 token(之前随便写了个 "11"):

	// 返回 token 给前端token, err := common.ReleaseToken(user)if err != nil {ctx.JSON(http.StatusInternalServerError, gin.H{"code": 500,"msg":  "系统异常",})log.Printf("token generate error : %v", err)return}// 返回结果ctx.JSON(200, gin.H{"code": 200,"data": gin.H{"token": token,},"msg": "登录成功",})

测试登录用户,拿到 token :

token 由三部分组成:

  • 协议头(token 使用的加密协议)
  • 我们给token中存储的信息(解密后是 JSON 格式的数据)
  • 前两部分加上key通过hash后的值

5.3、定义用户认证中间件

        如果不加中间件的话,前端请求时携带token,返回的 user 是 null ,因为我们没有往上下文存储 user 的信息。

        中间件为的是把前端请求时,authorization 中携带的 token 信息解析出来验证并保存到上下文。在 middleware 包下创建 AuthMiddleware.go:

package middlewareimport ("com.lyh/goessential/common""com.lyh/goessential/model""github.com/gin-gonic/gin""net/http""strings"
)// 自定义中间件用于用户验证:相当于SpringBoot中的拦截器
func AuthMiddleware() gin.HandlerFunc {return func(ctx *gin.Context) {// 获取 authorization headertokenString := ctx.GetHeader("Authorization")//eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySWQiOjIsImV4cCI6MTcxNTE1MDIyNCwiaWF0IjoxNzE0NTQ1NDI0LCJpc3MiOiJseWgiLCJzdWIiOiJ1c2VyIHRva2VuIn0.C6yH99IZDjj6_FnpHaREVPmoCX82nYWv1OZao171iPg// 验证格式if tokenString == "" || !strings.HasPrefix(tokenString, "Bearer ") {ctx.JSON(http.StatusUnauthorized, gin.H{"code": 401,"msg":  "权限不足",})ctx.Abort()return}tokenString = tokenString[7:] // Bearer+' '一共7位token, claims, err := common.ParseToken(tokenString)if err != nil || !token.Valid {ctx.JSON(http.StatusUnauthorized, gin.H{"code": 401,"msg":  "权限不足",})ctx.Abort()return}// 验证通过后获取claims 中的 userIduserId := claims.UserIdDB := common.GetDB()var user model.UserDB.First(&user, userId)// 用户不存在if user.ID == 0 {ctx.JSON(http.StatusUnauthorized, gin.H{"code": 401, "msg": "权限不足"})ctx.Abort()return}// 用户存在 将user信息写入上下文ctx.Set("user", user)ctx.Next()}
}

添加路由并测试

添加到我们的管理所有路由的文件(routes.go)中:

测试:

6、统一请求返回格式

        我们在学习 SpringBoot 项目的时候也进行了请求的统一返回格式:(code,data,other),这里也是一样,为的是简化开发,比如我们前面返回前端需要这样写:

    ctx.JSON(200, gin.H{"code": 200,"msg":  "注册成功",})

统一格式后我们只需要写个 200 和 "注册成功" 就可以了。

        此外,我们给前端返回的数据还有一些问题:比如把用户的全部信息都返回出去了(包括密码登隐私信息)

6.1、数据传输对象(dto)

创建一个包 dto 并创建 user_dto.go 用来将返回给前端的 user 转换为 userDto:

package dtoimport ("com.lyh/goessential/model"
)type UserDto struct {Name  string `json:"name"`Phone string `json:"phone"`
}// 将 user 转换为 userDto
func ToUserDto(user model.User) UserDto {return UserDto{Name:  user.Name,Phone: user.Phone,}
}

修改 controller 中的 Info 方法:

func Info(ctx *gin.Context) {// 从上下文中获得用户的信息user, _ := ctx.Get("user")ctx.JSON(http.StatusOK, gin.H{"code": 200, "data": gin.H{"user": dto.ToUserDto(user.(model.User))}})
}

再次进行用户验证:

可以看到,这次返回的数据没有其它敏感信息。

6.2、封装 HTTP 返回

创建目录 response,并创建 response.go :        

package responseimport ("github.com/gin-gonic/gin""net/http"
)// 这里的 code 是我们自定义的业务code
func Response(ctx *gin.Context, httpStatus int, code int, data gin.H, msg string) {ctx.JSON(httpStatus, gin.H{"code": code, "date": data, "msg": msg})
}func Success(ctx *gin.Context, data gin.H, msg string) {Response(ctx, http.StatusOK, 200, data, msg)
}func Fail(ctx *gin.Context, data gin.H, msg string) {Response(ctx, http.StatusOK, 400, data, msg)
}

        定义了统一的前端返回类型之后,我们就可以开始修改之前的返回代码了,之前我们的 HTTP 返回都是通过 ctx.JSON(httpStatus,gin.H) 来返回的,现在我们需要都替换为我们自定义的返回格式,比如下面的:

    ctx.JSON(http.StatusUnprocessableEntity, gin.H{"code": 422,"msg":  "手机号必须为11位!",})

统一之后就清爽多了,而且不会存在前端拿一些 JSON 的属性却拿不到的情况。 

response.Response(ctx, http.StatusUnprocessableEntity, 422, nil, "手机号必须为11位")

7、从文件中读取配置(viper)

        上面我们的很多配置信息都是直接定义在代码中的(比如连接数据库需要的参数),这样很不好管理和维护,所以这里我们统一下配置源。

7.1、安装 viper

go get github.com/spf13/viper

如果需要使用旧版本就去 go.mod 取修改版本号重新下载。

7.2、编写配置文件(yml)

在 config 目录下创建 application.yml:

server:port: 1016
datasource:driverName: mysqlhost: 127.0.0.1port: 3306database: go_webusername: rootpassword: Yan1029.charset: utf8mb4

7.3、使用 viper 读取配置文件

在 main 方法中添加读取配置文件的函数:

package mainimport ("com.lyh/goessential/common""github.com/gin-gonic/gin"_ "github.com/go-sql-driver/mysql""github.com/spf13/viper""os"
)func main() {InitConfig()db := common.InitDB()defer db.Close()engine := gin.Default()engine = CollectRoute(engine)port := viper.GetString("server.port")if port != "" {panic(engine.Run(":" + port))}panic(engine.Run()) // 默认端口 8080
}func InitConfig() {workDir, _ := os.Getwd()viper.SetConfigName("application")viper.SetConfigType("yml")viper.AddConfigPath(workDir + "/config")err := viper.ReadInConfig()if err != nil {panic(err)}
}

修改 databse.go 中的 InitDB 方法:

    driverName := viper.GetString("datasource.driverName")host := viper.GetString("datasource.host")port := viper.GetInt("datasource.port")database := viper.GetString("datasource.database")username := viper.GetString("datasource.username")password := viper.GetString("datasource.password")charset := viper.GetString("datasource.charset")

测试

数据库可以查询成功,配置成功。

注意事项 

1、gorm 版本问题

最新版 gorm:

使用旧版本的 gorm:

require (github.com/jinzhu/gorm v1.9.12
)

总结

        至此,我大概明白了 Go 语言怎么开发一个 Web 程序,也消除了我的很多疑虑,比如Java一个类就是一个文件,那Go语言怎么对项目进行分层架构等一些简单但又特别重要的内容。

        接下来,学学前端,至少了解怎么和后端交互,写一个功能完整的Web程序。 

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

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

相关文章

[论文阅读] 测试时间自适应TTA

最初接触 CVPR2024 TEA: Test-time Energy Adaptation [B站]&#xff08;1:35:00-1:53:00&#xff09;https://www.bilibili.com/video/BV1wx4y1v7Jb/?spm_id_from333.788&vd_source145b0308ef7fee4449f12e1adb7b9de2 实现&#xff1a; 读取预训练好的模型参数设计需要更…

Re69:读论文 LaMDA: Language Models for Dialog Applications

诸神缄默不语-个人CSDN博文目录 诸神缄默不语的论文阅读笔记和分类 论文名称&#xff1a;LaMDA: Language Models for Dialog Applications ArXiv网址&#xff1a;https://arxiv.org/abs/2201.08239 本文介绍谷歌提出的对话大模型LaMDA&#xff0c;主要关注对各项指标&#x…

一文带你了解 Oracle 23ai 新特性 Vector 的基础用法

Oracle Database 23ai 来了&#xff0c;虽然目前只是云上可商用&#xff0c;但是 OP 有 FREE 版本可以进行开发。 本文将介绍 Oracle 23ai 的新特性之一&#xff1a; AI 向量搜索&#xff0c;的部分内容。 向量数据类型 23ai 新增向量数据类型&#xff0c;可以用于表示一系列的…

【LeetCode刷题】875. 爱吃香蕉的珂珂

1. 题目链接 875. 爱吃香蕉的珂珂 2. 题目描述 3. 解题方法 简单的用我自己的理解来解释一下这道题的意思。 所以也就是说找到一个速度k&#xff0c;看还有没有比k更小的速度能吃完数组中的香蕉&#xff0c;如果有则继续寻找&#xff0c;没有则是k这个速度。就好比上面的解释…

成人职场英语口语柯桥外语培训之Big deal不是“大事”!别再翻译错啦!

关于deal&#xff0c; 其实有很多容易被人误解的表达&#xff0c; 小编今天就来给大家一一盘点~ 1, deal n. deal 作名词的时候意思是“交易&#xff1b;买卖”。 ❖ She got a new car for $1000! That was really a good deal! 她一千美金买了辆车&#xff01;真是158575…

图片编辑工具-Gimp

一、前言 GIMP&#xff08;GNU Image Manipulation Program&#xff09;是一款免费开源的图像编辑软件&#xff0c;具有功能强大和跨平台的特性。 GIMP作为一个图像编辑器&#xff0c;它提供了广泛的图像处理功能&#xff0c;包括但不限于照片修饰、图像合成以及创建艺术作品…

新的循环体和define

目录 do while讲解 练习&#xff1a; 结果&#xff1a; 分析&#xff1a; 定义&#xff1a;宏&#xff08;define&#xff09; 练习&#xff1a; 结果&#xff1a; 分析&#xff1a; define的优缺点 优点&#xff1a; 缺点&#xff1a; 作业&#xff1a; 大家假期…

搭建父模块和工具子模块

第一章 项目父模块搭建 1.1 nancal-idsa 作为所有工程的父工程&#xff0c;用于管理项目的所有依赖版本。 1.2 指定 pom 类型模块&#xff0c;删除 src 目录&#xff0c;点击Reload project 1.3 添加依赖 pom.xml <parent> <groupId>org.springframework.…

很快就可以试用Domino 15了

大家好&#xff0c;才是真的好。 前几天在比利时的安普卫特举办的Engage2024大会已经结束&#xff0c;流出的现场照片很多&#xff0c;主要是会议场地照片很多&#xff0c;说是令人震撼&#xff1b;可惜这次一手的PPT和会议内容不多.是的&#xff0c;本来我也是在等与会者写的…

人脸识别开源算法库和开源数据库

目录 1. 人脸识别开源算法库 1.1 OpenCV人脸识别模块 1.2 Dlib人脸识别模块 1.3 SeetaFace6 1.4 DeepFace 1.5 InsightFace 2. 人脸识别开源数据库 2.1 CelebA 2.2 LFW 2.3 MegaFace 2.4 Glint360K 2.5 WebFace260M 人脸识别 (Face Recognition) 是一种基于人的面部…

2022 年全国职业院校技能大赛高职组云计算赛项试卷(私有云)

#需要资源&#xff08;软件包及镜像&#xff09;或有问题的&#xff0c;可私聊博主&#xff01;&#xff01;&#xff01; #需要资源&#xff08;软件包及镜像&#xff09;或有问题的&#xff0c;可私聊博主&#xff01;&#xff01;&#xff01; #需要资源&#xff08;软件包…

代码随想录刷题随记30-贪心4

代码随想录刷题随记30-贪心4 860.柠檬水找零 leetcode链接 比较显然 class Solution {public boolean lemonadeChange(int[] bills) {int []accountnew int[3];for(int cur:bills){if(cur5)account[0];else if(cur10){account[0]--;if(account[0]<0)return false;account…

ICode国际青少年编程竞赛- Python-1级训练场-路线规划

ICode国际青少年编程竞赛- Python-1级训练场-路线规划 1、 Dev.step(3) Dev.turnLeft() Dev.step(4)2、 Dev.step(3) Dev.turnLeft() Dev.step(3) Dev.step(-6)3、 Dev.step(-2) Dev.step(4) Dev.turnLeft() Dev.step(3)4、 Dev.step(2) Spaceship.step(2) Dev.step(3)5、…

【论文阅读】Fuzz4All: Universal Fuzzing with Large Language Models

文章目录 摘要一、介绍二、Fuzz4All的方法2.1、自动提示2.1.1、自动提示算法2.1.2、自动提示的例子2.1.3、与现有自动提示技术的比较 2.2、fuzzing循环2.2.1、模糊循环算法2.2.2、Oracle 三、实验设计3.1、实现3.2、被测系统和baseline3.3、实验设置以及评估指标 四、结果分析4…

iPhone查看本机号码只需要这3招,不再为号码忘记犯愁!

在日常生活中&#xff0c;我们经常需要使用手机号码进行各种通讯活动&#xff0c;但有时候会忘记自己的手机号码&#xff0c;让人感到非常尴尬。不过&#xff0c;如果您是iPhone用户&#xff0c;那么您可以放心了&#xff01;因为在iphone查看本机号码只需要简单的几个步骤&…

linux系统 虚拟机的安装详细步骤

window&#xff1a; (1) 个人&#xff1a;win7 win10 win11 winxp (2)服务器&#xff1a;windows server2003 2008 2013 linux&#xff1a; (1)centos7 5 6 8 (2)redhat (3)ubuntu (4)kali 什么是linux: 主要是基于命令来完成各种操作&#xff0c;类似于DO…

使用STM32F103C8T6与蓝牙模块HC-05连接实现手机蓝牙控制LED灯

导言: 在现代智能家居系统中,远程控制设备变得越来越普遍和重要。本文将介绍如何利用STM32F103C8T6单片机和蓝牙模块HC-05实现远程控制LED灯的功能。通过这个简单的项目,可以学会如何将嵌入式系统与蓝牙通信技术相结合,实现远程控制的应用。 目录 导言: 准备工作: 硬…

Spring Data JPA的一对一、LazyInitializationException异常、一对多、多对多操作

Spring Data JPA系列 1、SpringBoot集成JPA及基本使用 2、Spring Data JPA Criteria查询、部分字段查询 3、Spring Data JPA数据批量插入、批量更新真的用对了吗 4、Spring Data JPA的一对一、LazyInitializationException异常、一对多、多对多操作 前言 通过前三篇Sprin…

GNU Radio创建FFT、IFFT C++ OOT块

文章目录 前言一、GNU Radio官方FFT弊端二、创建自定义的 C OOT 块1、创建 OOT 模块2、创建 OOT 块3、修改 C 和 CMAKE 文件4、编译及安装 OOT 块 三、测试1、grc 图2、运行结果①、时域波形对比②、频谱图对比 四、资源自取 前言 GNU Radio 自带的 FFT 模块使用起来不是很方便…

RT-DETR-20240507周更说明|更新Inner-IoU、Focal-IoU、Focaler-IoU等数十种IoU计算方式

RT-DETR改进专栏|包含主干、模块、注意力、损失函数等改进 专栏介绍 本专栏包含模块、卷积、检测头、损失等深度学习前沿改进,目前已有改进点70&#xff01;每周更新。 20240507更新说明&#xff1a; ⭐⭐ 更新CIoU、DIoU、MDPIoU、GIoU、EIoU、SIoU、ShapeIou、PowerfulIoU、…