Go开发及代码审计学习(一)

Go开发及代码审计学习(一)

进度一

# 一、Go 基本语法

和 js,python 语法类似,具体见菜鸟教程,C 语言中文网等,这里只记录难点

# (一) 数组

# 一维数组

  1. 初始化及遍历
1
2
3
4
5
6
7
8
9
10
11
var a [3]int             // 定义三个整数的数组
fmt.Println(a[0]) // 打印第一个元素
fmt.Println(a[len(a)-1]) // 打印最后一个元素
// 打印索引和元素
for i, v := range a {
fmt.Printf("%d %d\n", i, v) //打印索引和元素
}
// 仅打印元素
for _, v := range a {
fmt.Printf("%d\n", v)
}

在数组的定义中,如果在数组长度的位置出现 “…” 省略号,则表示数组的长度是根据初始化值的个数来计算,因此,上面数组 q 的定义可以简化为:

1
2
q := [...]int{1, 2, 3}
fmt.Printf("%T\n", q) // "[3]int"

# 循环遍历 range

1
2
3
4
5
6
// 创建一个整型切片,并赋值
slice := []int{10, 20, 30, 40}
// 迭代每一个元素,并显示其值
for index, value := range slice {
fmt.Printf("Index: %d Value: %d\n", index, value)
}
  1. 切片

根据索引位置取切片 slice 元素值时,取值范围是(0~len (slice)-1),超界会报运行时错误,生成切片时,结束位置可以填写 len (slice) 但不会报错。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var highRiseBuilding [30]int

for i := 0; i < 30; i++ {
highRiseBuilding[i] = i + 1
}

// 区间
fmt.Println(highRiseBuilding[10:15])

// 中间到尾部的所有元素
fmt.Println(highRiseBuilding[20:])

// 开头到中间指定位置的所有元素
fmt.Println(highRiseBuilding[:2])

结果:

1
2
3
[11 12 13 14 15]
[21 22 23 24 25 26 27 28 29 30]
[1 2]

动态创建切片,可以使用 make () 内建函数,格式如下:

1
make( []Type, size, cap )

其中 Type 是指切片的元素类型,size 指的是为这个类型分配多少个元素,cap 为预分配的元素数量,这个值设定后不影响 size,只是能提前分配空间,降低多次分配空间造成的性能问题。

1
2
3
4
5
a := make([]int, 2)
b := make([]int, 2, 10)

fmt.Println(a, b)
fmt.Println(len(a), len(b))

结果:

1
2
[0 0] [0 0]
2 2

其中 a 和 b 均是预分配 2 个元素的切片,只是 b 的内部存储空间已经分配了 10 个,但实际使用了 2 个元素。

容量不会影响当前的元素个数,因此 a 和 b 取 len 都是 2。

而为切片添加元素则与 python 类似,使用内建函数 append ()

1
2
3
4
var a []int
a = append(a, 1) // 追加1个元素
a = append(a, 1, 2, 3) // 追加多个元素, 手写解包方式
a = append(a, []int{1,2,3}...) // 追加一个切片, 切片需要解包

# 多维数组

声明:

1
2
3
4
5
6
7
8
// 声明一个二维整型数组,两个维度的长度分别是 4 和 2
var array [4][2]int
// 使用数组字面量来声明并初始化一个二维整型数组
array = [4][2]int{{10, 11}, {20, 21}, {30, 31}, {40, 41}}
// 声明并初始化数组中索引为 1 和 3 的元素
array = [4][2]int{1: {20, 21}, 3: {40, 41}}
// 声明并初始化数组中指定的元素
array = [4][2]int{1: {0: 20}, 3: {1: 41}}

# (二)函数

# 1. 声明

1
2
3
func 函数名(形式参数列表)(返回值列表){
函数体
}

如:

1
2
3
4
func hypot(x, y float64) float64 {
return math.Sqrt(x*x + y*y)
}
fmt.Println(hypot(3,4)) // "5"

# 2. 调用

函数在定义后,可以通过调用的方式,让当前代码跳转到被调用的函数中进行执行,调用前的函数局部变量都会被保存起来不会丢失,被调用的函数运行结束后,恢复到调用函数的下一行继续执行代码,之前的局部变量也能继续访问。

函数内的局部变量只能在函数体中使用,函数调用结束后,这些局部变量都会被释放并且失效。

Go 语言的函数调用格式如下:

1
返回值变量列表 = 函数名(参数列表)

例如,加法函数调用样式如下:result := add (1,1)

# 3. 匿名函数

# 1) 在定义时调用

1
2
3
func(data int) {
fmt.Println("hello", data)
}(100)

# 2) 赋值给变量

1
2
3
4
5
6
7
// 将匿名函数体保存到f()中
f := func(data int) {
fmt.Println("hello", data)
}

// 使用f()调用
f(100)

# 3) 用作回调函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import (
"fmt"
)

// 遍历切片的每个元素, 通过给定函数进行元素访问
func visit(list []int, f func(int)) { //封装visit

for _, v := range list {
f(v)
}
}

func main() {

// 使用匿名函数打印切片内容
visit([]int{1, 2, 3, 4}, func(v int) {
fmt.Println(v)
})
}
# 结合命令行

将匿名函数作为 map 的键值,通过命令行参数动态调用匿名函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package main

import (
"flag"
"fmt"
)

var skillParam = flag.String("skill", "", "skill to perform")

func main() {

flag.Parse()

var skill = map[string]func(){
"fire": func() {
fmt.Println("chicken fire")
},
"run": func() {
fmt.Println("soldier run")
},
"fly": func() {
fmt.Println("angel fly")
},
}

if f, ok := skill[*skillParam]; ok {
f()
} else {
fmt.Println("skill not found")
}

}

代码说明如下:

  • 第 8 行,定义命令行参数 skill,从命令行输入 --skill 可以将 = 后的字符串传入 skillParam 指针变量。
  • 第 12 行,解析命令行参数,解析完成后,skillParam 指针变量将指向命令行传入的值。
  • 第 14 行,定义一个从字符串映射到 func () 的 map,然后填充这个 map。
  • 第 15~23 行,初始化 map 的键值对,值为匿名函数。
  • 第 26 行,skillParam 是一个 *string 类型的指针变量,使用 *skillParam 获取到命令行传过来的值,并在 map 中查找对应命令行参数指定的字符串的函数。
  • 第 29 行,如果在 map 定义中存在这个参数就调用,否则打印 “技能没有找到”。

运行代码,结果如下:

1
2
3
4
PS D:\code> go run main.go --skill=fly
angel fly
PS D:\code> go run main.go --skill=run
soldier run

# (三) 接口调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package main

import (
"fmt"
)

// 调用器接口
type Invoker interface {
// 需要实现一个Call方法,调用时传入一个interface{}类型的变量
Call(interface{})
}

// 结构体类型
type Struct struct {
}

// 实现Invoker的Call
func (s *Struct) Call(p interface{}) {
fmt.Println("from struct", p) //打印字符串和接口p的值
}

// 函数定义为FuncCaller类型
type FuncCaller func(interface{})

// 实现Invoker的Call
func (f FuncCaller) Call(p interface{}) {
//函数的声明不能直接实现接口,需要将函数定义为类型后,使用类型实现结构体,当类型方法被调用时,还需要调用函数本体。
// 调用f函数本体
f(p)
}

func main() {

// 声明接口变量
var invoker Invoker

// 实例化结构体
s := new(Struct)

// 将实例化的结构体赋值到接口
invoker = s

// 使用接口调用实例化结构体的方法Struct.Call
invoker.Call("hello") // 'hello'=>call=>struct

// 将匿名函数转为FuncCaller类型,再赋值给接口
//将 func(v interface{}){} 匿名函数转换为 FuncCaller 类型(函数签名才能转换),此时 FuncCaller 类型实现了 Invoker 的 Call() 方法,赋值给 invoker 接口是成功的。
invoker = FuncCaller(func(v interface{}) {
fmt.Println("from function", v)
})

// 使用接口调用FuncCaller.Call,内部会调用函数本体
invoker.Call("hello")
}

# 二、一些 demo

# (一)go 读取 pdf 中的纯文本内容

  • 系统:windows10
  • 语言:Golang
  • 组件库:Bleve
  • golang 版本:1.17
  • 组件仓库:ledongthuc/pdf

# 1. 安装组件库

1
2
go get -u github.com/ledongthuc/pdf
go mod vendor

安装过程中报错:

1
connectex: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond. 

解决方法:

改成我们国内可用的代理地址,在命令提示符输入: go env -w GOPROXY=https://goproxy.cn, 然后再做各种操作就可以成功了。

# 2. 运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package main

import (
"bytes"
"fmt"

"github.com/ledongthuc/pdf"
)

func main() {
pdf.DebugOn = true
content, err := readPdf("test.pdf") // 读取测试pdf
if err != nil {
panic(err)
}
fmt.Println(content)
return
}

func readPdf(path string) (string, error) {
f, r, err := pdf.Open(path)
// remember close file
defer f.Close()
if err != nil {
return "", err
}
var buf bytes.Buffer
b, err := r.GetPlainText()
if err != nil {
return "", err
}
buf.ReadFrom(b)
return buf.String(), nil
}

运行结果如下:

image-20220525112704470

image-20220525112608472

# (二) Go 连接数据库

安装依赖

1
go get -u github.com/go-sql-driver/mysql

# 1. go 连接 MySQL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package main

import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql" //导入包但不使用,init()
)

//Go连接Mysql示例
func main() {
//数据库
//用户名:密码啊@tcp(ip:端口)/数据库的名字
dsn := "root:123456@tcp(127.0.0.1:3306)/testgo"
//连接数据集
db, err := sql.Open("mysql", dsn) //open不会检验用户名和密码
if err != nil {
fmt.Printf("dsn:%s invalid,err:%v\n", dsn, err)
return
}
err = db.Ping() //尝试连接数据库
if err != nil {
fmt.Printf("open %s faild,err:%v\n", dsn, err)
return
}
fmt.Println("连接数据库成功~")

}

# 实现增删查改

首先在 mysql 的 testgo 中创建 user 表,实例有三个字段

1
2
3
4
5
6
CREATE TABLE `user`(
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(20) DEFAULT '',
`age` INT(11) DEFAULT '0',
PRIMARY KEY (`id`)
)ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;

建表成功后如下:

image-20220527161851844

# 增加 DB

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package main

import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql" //导入包但不使用,init()
)

var db *sql.DB //连接池对象
func initDB() (err error) {
//数据库
//用户名:密码啊@tcp(ip:端口)/数据库的名字
dsn := "root:123456@tcp(127.0.0.1:3306)/testgo"
//连接数据集
db, err = sql.Open("mysql", dsn) //open不会检验用户名和密码
if err != nil {
return
}
err = db.Ping() //尝试连接数据库
if err != nil {
return
}
fmt.Println("连接数据库成功~")
//设置数据库连接池的最大连接数
db.SetMaxIdleConns(10)
return
}


func insert() {
sqlStr := `insert into user(name,age) values("加油呀",28)`//sql语句
ret, err := db.Exec(sqlStr)//执行sql语句
if err != nil {
fmt.Printf("insert failed,err:%v\n", err)
return
}
//如果是插入数据的操作,能够拿到插入数据的id
id, err := ret.LastInsertId()
if err != nil {
fmt.Printf("get id failed,err:%v\n", err)
return
}
fmt.Println("id", id)
}


//Go连接Mysql示例
func main() {
err := initDB()
if err != nil {
fmt.Printf("init DB failed,err%v\n", err)
}

//插入数据
insert()
}

# 删除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package main

import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql" //导入包但不使用,init()
)

var db *sql.DB //连接池对象
func initDB() (err error) {
//数据库
//用户名:密码啊@tcp(ip:端口)/数据库的名字
dsn := "root:123456@tcp(127.0.0.1:3306)/testgo"
//连接数据集
db, err = sql.Open("mysql", dsn) //open不会检验用户名和密码
if err != nil {
return
}
err = db.Ping() //尝试连接数据库
if err != nil {
return
}
fmt.Println("连接数据库成功~")
//设置数据库连接池的最大连接数
db.SetMaxIdleConns(10)
return
}
func deleteRow(id int) {
sqlStr := `delete from user where id=?`
ret, err := db.Exec(sqlStr, id)
if err != nil {
fmt.Printf("delete faild,err:%v\n", err)
return
}
n, _ := ret.RowsAffected()
fmt.Printf("删除了%d行数据\n", n)

}

//Go连接Mysql示例
func main() {
err := initDB()
if err != nil {
fmt.Printf("init DB failed,err%v\n", err)
}
//删除数据
deleteRow(1)
}

# 修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package main

import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql" //导入包但不使用,init()
)

var db *sql.DB //连接池对象
func initDB() (err error) {
//数据库
//用户名:密码啊@tcp(ip:端口)/数据库的名字
dsn := "root:123456@tcp(127.0.0.1:3306)/testgo"
//连接数据集
db, err = sql.Open("mysql", dsn) //open不会检验用户名和密码
if err != nil {
return
}
err = db.Ping() //尝试连接数据库
if err != nil {
return
}
fmt.Println("连接数据库成功~")
//设置数据库连接池的最大连接数
db.SetMaxIdleConns(10)
return
}

func updateRow(newAge int, id int) {
sqlStr := `update user set age=? where id=?`
ret, err := db.Exec(sqlStr, newAge, id)
if err != nil {
fmt.Printf("update failed ,err:%v\n", err)
return
}
n, _ := ret.RowsAffected()
fmt.Printf("更新了%d行数据\n", n)
}

//Go连接Mysql示例
func main() {
err := initDB()
if err != nil {
fmt.Printf("init DB failed,err%v\n", err)
}

//更新数据
updateRow(20, 1)

}

# 查找

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
package main

import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql" //导入包但不使用,init()
)

type user struct {
id int
name string
age int
}

var db *sql.DB //连接池对象
func initDB() (err error) {
//数据库
//用户名:密码啊@tcp(ip:端口)/数据库的名字
dsn := "root:123456@tcp(127.0.0.1:3306)/testgo"
//连接数据集
db, err = sql.Open("mysql", dsn) //open不会检验用户名和密码
if err != nil {
return
}
err = db.Ping() //尝试连接数据库
if err != nil {
return
}
fmt.Println("连接数据库成功~")
//设置数据库连接池的最大连接数
db.SetMaxIdleConns(10)
return
}
func query(id int) {
//1.查询单挑记录的sql语句 ?是参数
sqlStr := "select id,name,age from user where id=?;"
//2.执行
rowObj := db.QueryRow(sqlStr, id) //从连接池里取一个连接出来去数据库查询单挑记录
//3.拿到结果
var u1 user
rowObj.Scan(&u1.id, &u1.name, &u1.age)
//打印结果
fmt.Printf("u1:%#v\n", u1)
}

func queryMore(n int) {
//1.sql语句
sqlStr := "select id,name,age from user where id >?;"
//2.执行
rows, err := db.Query(sqlStr, n)
if err != nil {
fmt.Printf("%s query failed,err:%v\n", sqlStr, err)
return
}
//3一定要关闭rows
defer rows.Close()
//4.循环取值
for rows.Next() {
var u1 user
rows.Scan(&u1.id, &u1.name, &u1.age)
fmt.Printf("u1:%#v\n", u1)
}
}

//Go连接Mysql示例
func main() {
err := initDB()
if err != nil {
fmt.Printf("init DB failed,err%v\n", err)
}
//查询单行
query(3)
//查询多行
queryMore(0)
}

结果实例如下:

image-20220527162547853

# 2. GORM 连接数据库

# 2.1 下载依赖

1
go get -u github.com/jinzhu/gorm

image-20220527162814851

# 2.2 连接数据库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)

func main() {
//定义连接信息,用户名、密码、通信协议等信息
url := "root:123456@tcp(localhost:3306)/testgo?charset=utf8&parseTime=True&loc=Local"
//连接数据库,连接数据库时,可以加上一些高级配置,就是gorm.Config中的参数
_, err := gorm.Open(mysql.Open(url), &gorm.Config{})
if err != nil {
fmt.Println("连接失败")
return
} else {
fmt.Println("连接成功")
}
}

报错按照 GoLand 提示做就行,下载相应依赖

# 2.3 根据 go 创建表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package main

import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)

//创建user实体,用于通过实体创建sql表
type User struct {
UserId int `gorm:"primary_key"`
UserName string
UserSex string
}

func main() {
//1.连接数据库
dsn := "root:123456@tcp(localhost:3306)/testgo?charset=utf8&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
//判空
if err != nil {
fmt.Println("数据库连接失败")
return
}

//2.创建表,自动迁移(把结构体和数据表进行对应)
//db.AutoMigrate(&books{})
db.AutoMigrate(&User{})
}

# 2.4 插入数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package main

import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)

//创建user实体,用于通过实体创建sql表
type User struct {
UserId int `gorm:"primary_key"`
UserName string
UserSex string
}

func main() {
//1.连接数据库
dsn := "root:123456@tcp(localhost:3306)/testgo?charset=utf8&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
//判空
if err != nil {
fmt.Println("连接数据库失败")
return
}

//2.插入数据
user := User{1, "张三", "男"}
db.Create(user)
}

结果:

image-20220527163728001

# 2.5 查询

First&Take&Last

1
2
3
4
5
6
7
#First():通过主键进行升序排列,获取第一条数据
select * from table order by id limit 1
#Task():不通过列进行排序,直接获取第一条数据
select * from table limit 1
#Last():通过主键进行降序排列,获取第一条数据
select * from table order by id desc limit 1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package main

import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)

//创建books实体
type Books struct {
//注意:当实体和数据库名称不一致时,需要起别名来进行查询
BookID int `gorm:"column:bookID",gorm:"primary_key"`
BookName string `gorm:"column:bookName"`
BookCounts int `gorm:"column:bookCounts"`
Detail string `gorm:"column:detail"`
}

func main() {
//1.定义连接信息,用户名、密码、通信协议等信息
url := "root:123456@tcp(localhost:3306)/testgo?charset=utf8&parseTime=True&loc=Local"
//连接数据库,连接数据库时,可以加上一些高级配置,就是gorm.Config中的参数
db, err := gorm.Open(mysql.Open(url), &gorm.Config{})
if err != nil {
fmt.Println("连接失败")
return

}
//创建表
//db.AutoMigrate(&Books{})
//
//books := Books{
// 1, "少年派", 30, "test",
//}
////创建books
//db.Create(books)

//2.查询
var book Books
//2.1 db.First() 按照主键升序排列,取第一条数据
//select * from books order by bookId limit 1
//创建一个books对象,让查出来的数据赋值到这个对象中
db.First(&book)

//2.2 db.Take() 获取一条数据,没有指定排序字段
//select * from books limit 1
//db.Take(&book)
//fmt.Println(book)

//2.3 db.Last() 按照主键降序排列,取第一条数据
//select * from books order by bookId desc limit 1
db.Last(&book)
fmt.Println(book)
}

# 2.6 多记录查询

1
2
books :=[]Books{}
db.Find(&books)

# 2.7 条件查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//4.使用条件进行索引
//4.1 string条件
//4.1.1 获取满足条件并且按主键升序排列的首条记录
//var book Books
//sql:select * from books where bookName = 'Linux' order by id limit 1
//db.Where("bookName=?", "Linux").First(&book)
//fmt.Println(book)

//4.1.2 获取满足条件的所有记录
//books := []Books{}
//sql:select * from books where bookCounts=10
//db.Where("bookCounts", 10).Find(&books)
//fmt.Println(books)

//4.1.3 除过满足条件的记录,查询出其他所有记录
//books := []Books{}
////select * from books where bookName <> 'Linux'
//db.Where("bookName <> ?", "Linux").Find(&books)
//fmt.Println(books)

//4.1.4 模糊查询
//由于模糊查询可能是一条数据,也可能是多条数据,所以使用数组接收
//books := []Books{}
////sql:select * from books bookName like %Li%
//db.Where("bookName like ?", "%Li%").Find(&books)
//fmt.Println(books)

//4.1.5 多条件查询
//var book Books
//db.Where("bookName = ? and bookCounts = ?", "mysql", 10).Find(&book)
//fmt.Println(book)

//4.1.6 使用between
books := []Books{}
db.Where("bookCounts between ? and ?", 1, 20).Find(&books)
fmt.Println(books)

# 2.8 删除数据

1
2
3
4
5
6
//删除记录
//1.根据主键删除
//sql:delete from books where id =4
//db.Delete(Books{}, 4)
//2.根据条件删除
db.Where("bookName = ?", "Linux").Delete(Books{})

# 2.9 查询

1
2
3
4
5
6
7
8
9
//更新数据
//1.更新一列
//db.Model(Books{}).Where("bookName", "Java").Update("bookCounts", 15)
//2.更新多列
//2.1 使用struct更新
//db.Model(Books{}).Where("bookName", "Java").Updates(Books{BookCounts: 20, Detail: "Java狗都不学"})
//2.2 使用map更新
db.Model(Books{}).Where("bookName", "MySQL").Updates(map[string]interface{}{"BookCounts": "33", "Detail": "MySQL太难了"})

# 三、gin-vue-admin 项目学习

项目界面如下:

image-20220525150719882

项目架构:

系统架构图

前端架构:

前端详细设计图

因为有前端 vue 基础,所以目前主要根据官方文档学后端 server 架构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
├── api
│   └── v1
├── config
├── core
├── docs
├── global
├── initialize
│   └── internal
├── middleware
├── model
│   ├── request
│   └── response
├── packfile
├── resource
│   ├── excel
│   ├── page
│   └── template
├── router
├── service
├── source
└── utils
├── timer
└── upload

# 四、go 代码审计

目前在跟 Firebasky 师傅的 demo 进行学习

# 附录:go 新手向

go 推荐项目

go 学习视频

Goweb 搭建 vue+go+gin+mysql+redis 的博客论坛

基于 gin 框架和 gorm 的 web 开发实战

# 参考文章

https://blog.csdn.net/memory_qianxiao/article/details/109632778

https://juejin.cn/post/7095205230983249934

Author

y1seco

Posted on

2022-05-27

Updated on

2022-05-27

Licensed under

Comments

:D 一言句子获取中...