Files
StudyNote/语言/GO/go路径.md
2026-02-13 23:38:38 +08:00

61 KiB
Raw Blame History

D1

任务

  • 安装golang
  • 基本语法
    • 变量
    • 类型(基础类型 结构体 接口 切片 map....
    • 函数
    • 执政
    • 流程控制

数据类型

数字类型

序号 类型和描述
1 uint8 无符号 8 位整型 (0 到 255)
2 uint16 无符号 16 位整型 (0 到 65535)
3 uint32 无符号 32 位整型 (0 到 4294967295)
4 uint64 无符号 64 位整型 (0 到 18446744073709551615)
5 int8 有符号 8 位整型 (-128 到 127)
6 int16 有符号 16 位整型 (-32768 到 32767)
7 int32 有符号 32 位整型 (-2147483648 到 2147483647)
8 int64 有符号 64 位整型 (-9223372036854775808 到 9223372036854775807)

切片

对于数组的抽象 可变 变长动态数组

make([]T, length, capacity)
	_ = make([]int, 5) //创建一个长度&容量为5的切片
	// 创建一个整型切片
	// 其长度为 3 个元素,容量为 5 个元素
	_ = make([]int, 3, 5)
}

len 获取长度 cap获取容量

	fmt.Println("numbers[1:4] ==", numbers[1:4]) //1->4
	/* 默认下限为 0*/
	fmt.Println("numbers[:3] ==", numbers[:3]) //小于三的
	/* 默认上限为 len(s)*/
	fmt.Println("numbers[4:] ==", numbers[4:])//大于四的

copy() append

	num = append(num, 0)
	就是在切片后面加了0  也可以多个
		num = append(num, 4,5,3,1,6)

range

	for _, nums := range num {
		fmt.Println(nums)
	}

Map

maparr := make(map[int]int)
maparr[1] = 3
使用字面量创建 Map
m := map[string]int{
    "apple":  1,
    "banana": 2,
    "orange": 3,
} //初始化
delete(maparr, 1)
fmt.Print(maparr[1])

接口

package main

import "fmt"

// 定义接口
type area interface {
	add(id int) int
	delete(flag bool)
}

// 定义Book结构体并实现area接口的所有方法
type Book struct {
	id   int
	name string
}

// 实现area接口的add方法带Book接收者
func (i int) add(num int) int {
	return i + num
}

//报错中只有用户自定义的类型如通过type MyInt int创建的新类型才能绑定方法。内置的int、string等类型属于 “非本地类型”,不允许直接为其添加方法。

type MyInt int

func (i MyInt) add(num int) int {
	return int(i) + num
}

// 实现area接口的delete方法带Book接收者
func (b Book) delete(flag bool) {
	if flag {
		fmt.Println("删除成功")
	} else {
		fmt.Println("删除失败")
	}
}

func main() {
	// 创建实现了area接口的结构体实例
	var b area = Book{id: 1, name: "Go编程"}
	// 通过实例调用接口方法
	num := b.add(4)
	fmt.Println(num) // 输出5
	b.delete(true)   // 输出:删除成功
}


Errors

接口类型s

type error interface {
    Error() string
}

New() --> 返回一个错误消息

package main

import (
	"errors"
	"fmt"
)

func main() {
	err := errors.New("this is an error")
	fmt.Println(err) // 输出this is an error
}

自定义Error

package main

import (
	"errors"
	"fmt"
)
//建立结构体
type MyError struct {
	Code int
	Msg  string
}
//接口
func (e *MyError) Error() string {
	return fmt.Sprintf("Code: %d, Msg: %s", e.Code, e.Msg)
}

func getError() error {
	return &MyError{Code: 404, Msg: "Not Found"}
}

func main() {
	err := getError()
	var myErr *MyError
	if errors.As(err, &myErr) {
		fmt.Printf("Custom error - Code: %d, Msg: %s\n", myErr.Code, myErr.Msg)
	}
}

git

仓库初始化

git init

clone

git clone +地址

创建文件

echo "这是第一个测试文件" > test.txt  # 创建并写入内容

加到暂存区

git add test.txt

提交到版本库

git commit -m"解释说明"

创建新分支

git checkout -b newbranch

提交

echo "在feature分支添加新内容" >> test.txt
git add test.txt
git commit -m "feature分支修改test.txt添加新内容"

分支与maser合并

  • 先回到主分支

    • git checkout main
      
      • 将feature-branch合并到主分支

        git merge feature-branch
        
      • 若需要,将合并后的代码推送到远程仓库:

        git push -u origin master
        

      git remote add origin <远程仓库地址>

Go并发编程

Goroutine

程序员只需要定义很多个任务让系统去帮助我们把这些任务分配到CPU上实现并发执行呢

Go语言中的goroutine就是这样一种机制goroutine的概念类似于线程,但 goroutine是由Go的运行时runtime调度和管理的。Go程序会智能地将 goroutine 中的任务合理地分配给每个CPU。Go就是因为它在语言层面已经内置了调度和上下文切换的机制。

在func前面加一个go 实现线程

使用sync.WaitGroup线程同步

package main

import (
	"fmt"
	"sync"
)

var wg sync.WaitGroup

func hello(i int) {
	defer wg.Done()
	fmt.Println("hello GGGG", i)
}
func main() {
	for i := 0; i < 20; i++ {
		wg.Add(1)
		go hello(i)
	}
	wg.Wait()
}

goroutine是并发执行的而goroutine的调度是随机的。

wg.Add(1)

每次激活想要被等待完成的goroutine之前先调用Add(),用来设置或添加要等待完成的goroutine数量

wg.Done()

每次需要等待的goroutine在真正完成之前,应该调用该方法来人为表示goroutine完成了该方法会对等待计数器减1

wg.Wait()

在等待计数器减为0之前Wait()会一直阻塞当前的goroutine

image-20251117143100833

没用的后果 就是

image-20251117143123849

defer

defer是go中一种延迟调用机制defer后面的函数只有在当前函数执行完毕后才能执行将延迟的语句按defer的逆序进行执行也就是说先被defer的语句最后被执行最后被defer的语句最先被执行通常用于释放资源。

defer 语句通常用于确保一个函数调用在程序执行结束时发生,常见的用例包括文件关闭、锁释放、资源回收等

runtime

runtime.Gosched()

让当前正在运行的协程goroutine主动让出 CPU 时间片,将执行权交还给 Go 调度器,以便调度器可以安排其他等待运行的协程执行。

package main

import (
	"fmt"
	"runtime"
)

func main() {
	go func(s string) {
		for i := 0; i < 2; i++ {
			fmt.Println(s)
		}
	}("world")
	// 主协程
	for i := 0; i < 2; i++ {
		// 切一下,再次分配任务
		runtime.Gosched()
		fmt.Println("hello")
	}
}

这样的话就是每次要输出hello的时候先暂停一下 看看有没有其他的进程在执行 然后把自己的时间片让出来 给其他人

runtime.Goexit()

结束进程

runtime.GOMAXPROCS

多少个OS线程来同时执行Go代码 默认一般都是机器上的核心数

Channel

Go语言的并发模型是CSP,提倡通过通信共享内存而不是通过共享内存而实现通信。

channel是可以让一个goroutine发送特定值到另一个goroutine的通信机制。

类似队列 先进先出

ctnm这个channel是一个无缓冲通道 只能通一个接受一个放开 要不然不能 会产生死锁

可以没有发送 但是必须有接受

package main

import (
	"fmt"
	"time"
)

func recv(c chan int) {
	ret := <-c
	fmt.Println("接收成功", ret)
}
func main() {
	ch := make(chan int)
	go recv(ch)

	time.Sleep(10 * time.Second) // 等待子协程就绪
	ch <- 10
	fmt.Println("发送成功")

	time.Sleep(30 * time.Second) // 等待子协程打印完成
}

当然也有 有缓冲的

	ch :=make(chan int ,34)

优雅的关闭通道

close(ch)

如何优雅的从通道循环取值

package main

import "fmt"

//
//func main() {
//	ch := make(chan int, 1) // 创建一个容量为1的有缓冲区通道
//	ch <- 10
//	fmt.Println("发送成功")
//
//}

func main() {
	ch1 := make(chan int)
	ch2 := make(chan int)
	go func() {
		for i := 0; i < 100; i++ {
			ch1 <- i
		}
		close(ch1)
	}()

	go func() {
		for {
			i, ok := <-ch1 // 通道关闭后再取值ok=false
			if !ok {
				break
			}
			ch2 <- i * i
		}
		close(ch2)
	}()
	for i := range ch2 { // 通道关闭后会退出for range循环
		fmt.Println(i)
	}

}

单向通道

var ch2 chan<- float64 只用于写
var ch3 <-chan int 只用于读

select

虽然确实我们可以

for{
    // 尝试从ch1接收值
    data, ok := <-ch1
    // 尝试从ch2接收值
    data, ok := <-ch2
    
}

通过这个方式来实现从多个通道来对接受值接受需求 但是这么做一点都不优雅

所以Go内置了一个select关键字来对于这个场景 来让这个更加优雅 可以同时操作多个通道

	chan1:=make(chan int ,4)
	chan2:=make(chan int ,4)
	
	select {
	case <-chan1:
		// 如果chan1成功读到数据则进行该case处理语句
	case chan2 <- 1:
		// 如果成功向chan2写入数据则进行该case处理语句
	default:
		// 如果上面都没有成功则进入default处理流程
	}

select可以同时监听一个或多个channel直到其中一个channel ready

package main

import (
	"fmt"
	"time"
)

func test1(ch chan string) {
	time.Sleep(time.Second * 5)
	ch <- "test1"
}
func test2(ch chan string) {
	time.Sleep(time.Second * 10)
	ch <- "test2"
}

func main() {
	// 2个管道
	output1 := make(chan string)
	output2 := make(chan string)
	// 跑2个子协程写数据
	go test1(output1)
	go test2(output2)
	// 用select监控
	select {
	case _ = <-output1:
		fmt.Println("s1先写完")
	case s2 := <-output2:
		fmt.Println("s2=", s2)
	}
}

GC

了解

常用库

fmt

  • Print

    • func Print(a ...interface{}) (n int, err error) 
      func Printf(format string, a ...interface{}) (n int, err error)
      func Println(a ...interface{}) (n int, err error)
      
  • Fprint ****

常用这个函数往文件中写入内容

// 向标准输出写入内容
fmt.Fprintln(os.Stdout, "向标准输出写入内容")
fileObj, err := os.OpenFile("./xx.txt", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
    fmt.Println("打开文件出错err:", err)
    return
}
name := "枯藤"
// 向打开的文件句柄中写入内容
fmt.Fprintf(fileObj, "往文件中写如信息:%s", name)
  • os.O_CREATE:如果目标文件不存在,则自动创建该文件;如果文件已存在,则不影响(不会覆盖)。
  • os.O_WRONLY:以只写模式打开文件(只能向文件写入数据,不能读取)。
  • os.O_APPEND:以追加模式写入数据(新写入的内容会添加到文件末尾,而不是覆盖原有内容)。

0644:文件权限(仅在文件被创建时生效)

权限值由 3 组数字组成,分别对应:文件所有者owner所属组group其他用户others 的权限。 八进制 0644 中,前缀 0 表示这是八进制数,后面的 6、4、4 分别对应三组权限: 第一位 6所有者权限6 = 4读权限 + 2写权限 → 所有者可以读和写该文件。 第二位 4所属组权限4 = 4读权限 → 同组用户只能读该文件,不能写。 第三位 4其他用户权限4 = 4读权限 → 其他用户只能读该文件,不能写。

Sprint

Sprint系列函数会把传入的数据生成并返回一个字符串。

没什么好说的 跟printf差不多

Errorf

是Go标准库中的函数可以创建一个新的错误。这个函数接受一个格式化字符串和一些参数返回一个新的错误 的优点在于其支持格式化字符串,这使得我们可以方便地在错误信息中包含一些动态的数据

Scan

 fmt.Scan(&name, &age, &married)

Scanf

	fmt.Scanf("1:%s 2:%d 3:%t", &name, &age, &married)
	强制要求按照格式输入不然不给过

Scanln

在遇到换行时才停止扫描

Io

文件开

file, err := os.Open("./main.go") 返回值 一个是
记得关
file.Close()

写文件

package main

import (
	"fmt"
	"os"
)

func main() {
	file, err := os.Create("./xxx.txt")
	if err != nil {
		fmt.Println(err)
		return
	}
	defer file.Close()
	for i := 0; i < 5; i++ {
		file.WriteString("ab\n")
		file.Write([]byte("cd\n"))
	}
}

Http

get

func Get(url string) (resp *Response, err error)

// 发送 GET 请求(语法糖)
resp, err := http.Get("https://httpbin.org/get")
if err != nil {
    panic(err)
}

package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
)

func main() {
    resp, err := http.Get("https://www.5lmh.com/")
    if err != nil {
        fmt.Println("get failed, err:", err)
        return
    }
    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        fmt.Println("read from resp.Body failed,err:", err)
        return
    }
    fmt.Print(string(body))
}

func main() {
    apiUrl := "http://127.0.0.1:9090/get"
    // URL param
    data := url.Values{}
    data.Set("name", "枯藤")
    data.Set("age", "18")
    u, err := url.ParseRequestURI(apiUrl)
    if err != nil {
        fmt.Printf("parse url requestUrl failed,err:%v\n", err)
    }
    u.RawQuery = data.Encode() // URL encode
    fmt.Println(u.String())
    resp, err := http.Get(u.String())
    if err != nil {
        fmt.Println("post failed, err:%v\n", err)
        return
    }
    defer resp.Body.Close()
    b, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        fmt.Println("get resp failed,err:%v\n", err)
        return
    }
    fmt.Println(string(b))
}

post

func Post(url, contentType string, body io.Reader) (resp *Response, err error)

func main() { // 发送 POST 请求(语法糖)
	resp, err := http.Post("https://httpbin.org/post", "application/json", nil)
	if err != nil {
		panic(err)
	}
	fmt.Println(resp)
}

优雅的写url

	req, err := http.NewRequest(http.MethodGet, "https://httpbin.org/get", nil)
	if err != nil {
		return
	}
	param := make(url.Values)
	param.Set("key", "1")//覆盖
	param.Add("key2", "2")//追加

	_, err = http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}

优雅的写Request

	// 1. 定义请求体内容JSON 字符串)
	jsonBody := `{"name": "张三", "age": 20}`
	// 2. 将 JSON 字符串转为 io.Reader 类型(用 bytes.Buffer 包装)
	bodyReader := bytes.NewBufferString(jsonBody)

	// 3. 创建请求注意POST 方法更适合带请求体)
	req, err := http.NewRequest("POST", "https://httpbin.org/post", bodyReader)	
// 创建 GET 请求
	req, err := http.NewRequest(http.MethodGet, "https://httpbin.org/get", nil)
	if err != nil {
		panic(err)
	}

	// 设置请求头
	req.Header.Add("Accept", "*/*")
	req.Header.Add("Accept-Language", "en-US,en;q=0.9")
	req.Header.Add("Authorization", "Token 12345")
	req.Header.Add("User-Agent", "Go-net/http")

	// 发送请求
	_, err = http.DefaultClient.Do(req) //发送 Do
	if err != nil {
		panic(err)
	}

百度

package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
)

func main() {

    url := fmt.Sprintf("https://www.baidu.com/s?wd=济南")
    resp, err := http.Get(url)//这个Get会自动的帮你发送 也就是Do
    if err != nil {
       panic(fmt.Sprintf("ulr有错误 %s", err))
    }
    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
       panic(fmt.Sprintf("body有错误 %s", err))
    }
    fmt.Println(string(body))
    
}

Json

json.Marshal(v any)
这样就可以让一个结构体变成json格式 返回的是一个切片
	b, err = json.MarshalIndent(p, "", "     ")//p prefix indent  -->   prefix 定义前缀字符, indent 设置缩进
	if err != nil {
		fmt.Println("json err ", err)
	}
	fmt.Println(string(b))
这个是带前缀的

D2

Http

工作原理

  1. 客户端通过tcp和服务器及逆行联系以后443并且在一般的tcp链接握手中请求证书
  2. 服务器返回公钥
  3. 客户端产生随机密钥
  4. 客户端使用公钥对 对称密钥进行加密
  5. 向服务端发送对称密钥
  6. 双方通过对称加密进行通信

请求方法

1 GET 从服务器获取资源。用于请求数据而不对数据进行更改。例如,从服务器获取网页、图片等。
2 POST 向服务器发送数据以创建新资源。常用于提交表单数据或上传文件。发送的数据包含在请求体中。
3 PUT 向服务器发送数据以更新现有资源。如果资源不存在,则创建新的资源。与 POST 不同PUT 通常是幂等的,即多次执行相同的 PUT 请求不会产生不同的结果

权限码

  • 1XX 消息响应

  • 2XX 成功

  • 3XX 重定向

  • 4XX 客户端错误

    • 400 客户端请求的语法错误,服务器无法理解
    • 401请求要求用户的身份认证
    • 保留
    • 403权限
    • 404网页无响应
    • 405 方法禁止
  • 5XX 服务器错误

    • 503 服务器不可用

报文

  1. 请求行
    1. 请求方法GET、POST
    2. 请求目标:通常是一个 URL ,表明了要操作的资源。
    3. 版本号HTTP 协议版本
    4. 例子 -->GET / HTTP/1.1
  2. 请求头 -->一群kv值
  3. 请求体
    1. 介绍响应报文 --》状态码就在这里
    2. 首部字段
    3. 介绍功能

练习

	// 设置方法url添加header
	req, err := http.NewRequest("GET", "https://example.com", nil)
	//req, err := http.Get("https://example.com")
	if err != nil {
		fmt.Println("Error creating HTTP request:", err)
		return
	}
	req.Header.Add("Authorization", "Bearer <token>")
	req.Header.Add("Content-Type", "application/json")
	

	resp, err := http.Get("https://httpbin.org/get")
	if err != nil {
		fmt.Println("请求失败:", err)
		return
	}
	defer resp.Body.Close()

	// 1. 获取单个响应头的值(最常用)
	contentType := resp.Header.Get("Content-Type")
	contentLength := resp.Header.Get("Content-Length")
	date := resp.Header.Get("Date")

	fmt.Println("Content-Type:", contentType)     // 响应体格式(如 application/json
	fmt.Println("Content-Length:", contentLength) // 响应体长度(字节数)
	fmt.Println("Date:", date)                    // 服务器响应时间

	// 2. 如果头字段有多个值(例如 Set-Cookie 可能有多个)
	cookies := resp.Header["Set-Cookie"] // 返回 []string
	fmt.Println("所有 Cookie:")
	for _, cookie := range cookies {
		fmt.Println("-", cookie)
	}

	// 读取响应体(不影响头信息获取)
	body, _ := io.ReadAll(resp.Body)
	fmt.Println("\n响应体内容:", string(body))


/*
	resp, err := http.Get("https://httpbin.org/get") 发送请求 返回一个resp
		req, err := http.NewRequest("GET", "https://example.com", nil) 返回的是req 没有发送
*/

星辰英语本

项目初始化

 go install github.com/gogf/gf/cmd/gf/v2@latest
gf init 项目名字
go get -u github.com/gogf/gf/contrib/drivers/mysql/v2  //下载mysql驱动

在main。go中引入

    _ "github.com/gogf/gf/contrib/drivers/mysql/v2"

修改配置

hack/conig

gfcli:
  gen:
    dao:
      - link: "mysql:root:123456@tcp(127.0.0.1:3306)/exe?charset=utf8mb4&parseTime=true&loc=Local"

使用命令gf gen dao 就可以生成

注册接口

  1. 在api/users/v1/users.go中

    package v1
    
    import "github.com/gogf/gf/v2/frame/g"
    
    type RegisterReq struct {
    	g.Meta   `path:"users/register" method:"post"`
    	Username string `json:"username"`
    	Password string `json:"password"`
    	Email    string `json:"email"`
    }
    type RegisterRes struct {
    }
    
    

然后执行命令

gf gen ctrl

Logic 是业务逻辑层 类似java中的impl

	package users

import (
	"context"
	"star/internal/dao"
	"star/internal/model/do"
)

func (u *Users) Register(cxt context.Context, username, password, email string) error {
	_, err := dao.Users.Ctx(cxt).Data(do.Users{
		Username: username,
		Password: password,
		Email:    email,
	}).Insert()
	if err != nil {
		return err
	}
	return err
}

entity 业务实体抽象,承载业务数据 可能不直接对应表 业务层数据传递(入参、返回值)
do 数据库表的直接映射 与表结构完全对应 ORM 交互(数据读写的载体)
dao 封装数据库操作逻辑 负责执行 SQL/ORM 操作 业务层调用,实现数据访问
dao.Users.DB()//*gdb.DBGoFrame 的数据库连接对象)。 用于执行底层数据库操作(如原生 SQL、事务控制等
dao.Users.Ctx() //GoFrame 的查询模型对象)
//返回一个与当前 DAO 绑定的查询模型,用于通过链式操作构建数据库查询(如条件筛选、排序、分页等)。
dao.Users.Ctx().Data()
//是 “数据容器”,负责承载插入 / 更新时的具体数据。

用户重复检验

func (u *User) checkUser(cxt context.Context, username string) error {
	count, err := dao.User.Ctx(cxt).Where("username", username).Count()
	if err != nil {
		return err
	}
	if count > 0 {
		return gerror.New("用户已存在")
	}
	return nil
}

JWT

中间件

该方式也是主流的 WebServer 提供的请求流程控制方式, 基于中间件设计可以为 WebServer 提供更灵活强大的插件机制。

package middleware

import (
    "net/http"

    "star/internal/consts"

    "github.com/gogf/gf/v2/net/ghttp"
    "github.com/golang-jwt/jwt/v5"
)

func Auth(r *ghttp.Request) {
    var tokenString = r.Header.Get("Authorization")
    token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
        return []byte(consts.JwtKey), nil
    })
    if err != nil || !token.Valid {
        r.Response.WriteStatus(http.StatusForbidden)
        r.Exit()
    }
    r.Middleware.Next()
}

r.Middleware.Next() 这段代码是中间件的控制核心,放在函数的最后面表示它是一个前置中间件,代表请求前调用。放在最前面则是后置中间件,在请求后生效。

r.Header.Get("Authorization")HTTP Header中获取Authorization字段,即获取前端传过来的Tokenjwt.Parse 解析Token后再通过token.Valid验证是否有效,如果失效则返回 HTTP StatusForbidden 403 状态码,即权限不足,反之调用r.Middleware.Next()进入下一步。

Logic中不能直接获取HTTP对象,需要使用g.RequestFromCtx(ctx).Request

D3

redis

# Redis 配置示例
redis:
  # 单实例配置示例1
  default:
    address: 127.0.0.1:6379
    db:      1

  # 单实例配置示例2
  cache:
    address:     127.0.0.1:6379
    db:          1
    pass:        123456
    idleTimeout: 600

使用

g.Redis() 获取 Redis 客户端对象:
redis := g.Redis("cache")
	var ctx = gctx.New()//获取ctx
	_, err := g.Redis().Set(ctx, "key", "value")//set 操作
	if err != nil {
		g.Log().Fatal(ctx, err)
	}
	value, err := g.Redis().Get(ctx, "key")//取数据
	if err != nil {
		g.Log().Fatal(ctx, err)
	}
	fmt.Println(value.String())
// 执行 SET 命令
result, err := redis.Do(ctx, "SET", "key", "value")

// 执行 GET 命令
value, err := redis.Do(ctx, "GET", "key")

// 执行 HSET 命令
result, err := redis.Do(ctx, "HSET", "hash", "field", "value")

// 执行 HGETALL 命令
hash, err := redis.Do(ctx, "HGETALL", "hash")
// 执行 SET 命令 
result, err := redis.Set(ctx, "key", "value")

// 执行 GET 命令
value, err := redis.Get(ctx, "key")

// 执行 HSET 命令
result, err := redis.HSet(ctx, "hash", "field", "value")

// 执行 HGETALL 命令  
hash, err := redis.HGetAll(ctx, "hash")

Lunix

ls: 列出目录

cd切换目录

pwd显示目前的目录

mkdir创建一个新的目录

rmdir删除一个空的目录

cp: 复制文件或目录

cp /root/install.sh /home -->制 root目录下的install.sh 到 home目录下

rm: 移除文件或目录

经典 rm -rf

mv: 移动文件与目录,或修改文件与目录的名称

编辑文件

vi /vim

vi world.txt  # 输入i  a 进入编辑模式
# 进入编辑模式,输入内容
hello linux world !
# 保存并退出
:wq 

i 切换到输入模式,以输入字符。

x 删除当前光标所在处的字符。

: 切换到底线命令模式,以在最底一行输入命令

查看日志

  1. tail -->tail 命令可以用于查看日志文件的最后几行或实时追踪日志文件。
    • -n: 指定显示行数。 -f: 以跟随模式运行。 -c: 指定要显示的字符数。 -r: 从文件末尾倒序显示。
    • tail -n 100 server.log 看后100行
  2. head --> 看前几行 跟tail一样
  3. cat 全文搜索
    • cat server.log 看全部文件

4.grep命令—查找文件内容

grep -5 'parttern' inputfile 查找 符合的前后5行

查看系统状态

top 看实时监控 CPU、内存、进程等

image-20251121090649806

df -h 查看所有磁盘分区的总容量、已用、可用

du -sh 目录 查看目录的总磁盘占用

lsof -i 端口号's查看所有网络接口的 IP 地址、MAC 地址、网络状态等

端口监听

grep 端口监听

netstat -an 网络连接状态

压缩解压

  • 压缩zip test.zip test/
  • 解压unzip test.zip

用户

添加账号 useradd 选项 用户名

-c comment 指定一段注释性描述。

-d 目录 指定用户主目录,如果此目录不存在,则同时使用-m选项可以创建主目录。

-g 用户组 指定用户所属的用户组。

-G 用户组,用户组 指定用户所属的附加组。

-m 使用者目录如不存在则自动建立。

-s Shell文件 指定用户的登录Shell。

-u 用户号 指定用户的用户号,如果同时有-o选项则可以重复使用其他用户的标识号。

删除用户 userdel 选项 用户名

修改帐号 usermod 选项 用户名

文件权限

chmod 权限 文件

权限

租赁关系

在多租户架构中,不同租户贡共享一个应用程序实例及其基础设置。这就意味着所有租户使用的硬件资源都被统一管理(包含硬件的型号也能得到统一),不再需要像独立部署模式中的一样分散管理

租户关系是互联网技术领域中,特别是在云计算和 SaaS (软件即服务) 架构下的一种核心概念,指多个客户 (租户) 共享同一套软件系统和基础设施,同时各自的数据和配置相互隔离的关系模式。

特点 :共享软件实例 资源

物模型

是物联网领域中对物理设备或虚拟实体进行标准化、结构化定义的数字抽象模型,是物理实体在数字世界的 "数字孪生体"。

设备提供一套统一的 "数字说明书"

本质 JSON

三大核心组件

  1. 属性

字段名称 字段说明 约束条件
名称 参数中文名 “仅支持中文、英文大小写、数字、部分常用符号下划线减号括弧空格必须以中文、英文或数字开头长度不超过40个字符。”
标识符 参数唯一英文标识 支持大小写字母、数字和下划线、不超过50个字符。
数据类型 必选,可选整数型、浮点型、枚举型、字符串。
枚举项 枚举值和解释 仅枚举值参数。 分为参数值和参数描述参数值支持整形不超过2个字符参数描述支持中文、英文、数字、下划线不超过20个字符枚举项数量可自定义。
取值范围 数据范围 仅整形、浮点数。 可自定义,输入的数值范围不超过各类型数据所能表示的范围。
步长 取值间隔 仅整形、浮点数。 步长是指设备上报或下发数值时递增或递减的间隔。步长只能是一个正数整数型最小步长为1浮点数最小步长为10^(-7);最大步长不能超出取值范围的差值。
数据长度 字符串长度 仅字符串参数。 整数表示字符串最大长度取值1-2048
单位 数据单位
读写权限 读写权限 可选“读”“写”“读写” 表示参数的读写权限
  1. 方法

设备可执行的操作或功能,需要外部触发,通常有输入和输出参数

字段名称 字段说明 约束条件
名称 参数中文名 仅支持中文、英文大小写、数字、部分常用符号下划线减号括弧空格必须以中文、英文或数字开头长度不超过40个字符。
标识符 参数唯一英文标识 支持大小写字母、数字和下划线、不超过50个字符。
调用方式 异步调用是指云端执行调用后直接返回,不会关心设备的回复消息,如果服务为同步调用,云端会等待设备回复,否则会调用超时。 异步调用或同步调用任选其一。
输入参数 输入参数只可选择当前设备的属性,可多选,可为空。
输出参数 输出参数只可选择当前设备的属性,可多选,可为空。
描述 参数描述 100字以内
  1. 事件

    备在特定条件下主动上报的信息,用于通知外部系统状态变化或异常

字段名称 字段说明 约束条件
名称 参数中文名 仅支持中文、英文大小写、数字、部分常用符号下划线减号括弧空格必须以中文、英文或数字开头长度不超过40个字符。
标识符 参数唯一英文标识 支持大小写字母、数字和下划线、不超过50个字符。
输出参数 输出参数只可选择当前设备的属性,可多选,可为空。
描述 参数描述 100字以内

2. 典型应用场景

智能家居:环境监测系统

物模型核心定义

  • 属性:温度、湿度、光照强度、空气质量指数 (PM2.5)
  • 服务"设置告警阈值"、"校准传感器"
  • 事件"温湿度异常告警"、"空气质量恶化通知"

实际应用:当传感器检测到室内温度超过 30℃时自动触发事件上报系统可联动空调开启并发送通知给用户。

Modbus通信协议详解工业自动化的经典协议

DEF:串行通信协议,是全球第一个真正用于工业现场的总线协议 设备之间的数据交换

image-20251119164140610 image-20251119164235434

Modbus帧结构

Modbus协议的数据帧结构分为以下几个部分

  1. 设备地址段:标识目标设备的地址。
  2. 功能代码段:标识操作类型(如读、写)。
  3. 数据段:包含具体的操作数据。
  4. 错误校验段:用于检测数据传输是否正确。

Modbus协议支持多种传输方式

  • 串口传输如RS-232、RS-485。
  • 网络传输如Modbus TCP/IP。

CRC16校验

CRC16校验是一种常用的错误检测算法用于确保数据传输的可靠性。发送方和接收方使用相同的算法计算校验值并进行对比。

MQTT协议

发布者 代理 订阅者

MQTT使用的发布/订阅消息模式,它提供了一对多的消息分发机制,从而实现与应用程序的解耦。

这是一种消息传递模式,消息不是直接从发送器发送到接收器(即点对点),而是由MQTT server分发的。

image-20251119165257227

服务器分发消息,因此必须是发布者,但绝不是订阅者

客户端可以发布消息(发送方)、订阅消息(接收方)或两者兼而有之。

客户端(也称为节点)是一种智能设备,如微控制器或具有 TCP/IP 堆栈和实现 MQTT 协议的软件的计算机。

QoSQuality of Service levels

服务质量

MQTT 在这里帮助避免信息丢失及其服务质量水平

QoS 0

这一级别会发生消息丢失或重复消息发布依赖于底层TCP/IP网络。即<=1

QoS 1

QoS 1 承诺消息将至少传送一次给订阅者。

QoS 2

使用 QoS 2我们保证消息仅传送到目的地一次。

带有唯一消息 ID 的消息会存储两次首先来自发送者然后是接收者。QoS 级别 2 在网络中具有最高的开销,因为在发送方和接收方之间需要两个流。

image-20251119165649783

MQTT 数据包结构

  • 固定头Fixed header表示数据包类型及数据包的分组类标识;
  • 可变头Variable header数据包类型决定了可变头是否存在及其具体内容;
  • 消息体Payload,表示客户端收到的具体内容;
image-20251119165752497

固定头

  • 消息类型
  • 标识位 / DUP
    • 在不使用标识位的消息类型中,标识位被作为保留位。如果收到无效的标志时,接收端必须关闭网络连接:
  • QOS 服务保证
  • RET
    • 发布保留标识,表示服务器要保留这次推送的信息,如果有新的订阅者出现,就把这消息推送给它,如果设有那么推送至当前订阅者后释放。
  • 剩余长度

可变头

可变头的内容因数据包类型而不同,较常的应用是做为包的标识:

消息体

  • CONNECT消息体内容主要是客户端的ClientID、订阅的Topic、Message以及用户名和密码
  • SUBSCRIBE,消息体内容是一系列的要订阅的主题以及QoS
  • SUBACK,消息体内容是服务器对于SUBSCRIBE所申请的主题及QoS进行确认和回复。
  • UNSUBSCRIBE,消息体内容是要订阅的主题。

D4

掌握g.cfg()

//func exe() {
//
//	var ctx = gctx.New()
//	fmt.Println(g.Cfg().Get(ctx, "viewpath"))
//	fmt.Println(g.Cfg().Get(ctx, "database.default.0.role"))
//
//	var ctx = gctx.New()
//	fmt.Println(gcfg.Instance().Get(ctx, "viewpath"))
//	fmt.Println(gcfg.Instance().Get(ctx, "database.default.0.role"))
//}

g.log()

	ctx := context.Background()
	g.Log().Debug(ctx, "这是一条DEBUG级别的日志")
	g.Log().Info(ctx, "这是一条INFO级别的日志")
	g.Log().Error(ctx, "这是一条ERROR级别的日志")

golang操作数据库

package main

import (
	"database/sql"
	"fmt"
	"log"

	_ "github.com/go-sql-driver/mysql"
)

// 定义一个结构体来存储查询结果假设你的表有id, userName, passWord字段
type User struct {
	id       int
	userName string
	passWord string // 使用sql.NullInt64处理可能为NULL的值
}

func main() {
	// 连接数据库
	db, err := sql.Open("mysql",
		"root:123456@tcp(127.0.0.1:3306)/exe?charset=utf8mb4&parseTime=true&loc=Local")
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	// 验证连接
	err = db.Ping()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("数据库连接成功!")

	// 1. 基本查询(查询所有用户)
	fmt.Println("\n=== 查询所有用户 ===")
	rows, err := db.Query("SELECT id, userName, passWord FROM user")
	if err != nil {
		log.Fatal(err)
	}
	defer rows.Close()

	// 遍历查询结果
	for rows.Next() {
		var user User
		// 将查询结果扫描到结构体变量中
		err := rows.Scan(&user.id, &user.userName, &user.passWord)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Printf("id: %d, userName: %s, passWord: %s\n", user.id, user.userName, user.passWord)
	}
}

json序列化和反序列化

// package main
//
// import (
//
//  "encoding/json"
//  "fmt"
//
// )
//
//  type Person struct {
//     Name  string
//     Hobby string
//  }
//
//  func main() {
//     p := Person{"5lmh.com", "女"}
//     // 编码json
//     b, err := json.Marshal(p)
//     if err != nil {
//        fmt.Println("json err ", err)
//     }
//     fmt.Println(string(b))
//
//     // 格式化输出
//     b, err = json.MarshalIndent(p, "", "     ")
//     if err != nil {
//        fmt.Println("json err ", err)
//     }
//     fmt.Println(string(b))
//  }
package main

import (
    "encoding/json"
    "fmt"
    "os"
)

// 定义一个示例结构体(需序列化的对象)
type Person struct {
    Name string `json:"name"` // JSON 字段名映射
    Age  int    `json:"age"`
}

// 序列化结构体为 JSON 并写入文件
func writeToFile(data Person, filePath string) error {
    // 将结构体转换为 JSON 字节
    jsonData, err := json.MarshalIndent(data, "", "  ")
    if err != nil {
       return fmt.Errorf("序列化 JSON 失败: %v", err)
    }

    // 将 JSON 字节写入文件
    err = os.WriteFile(filePath, jsonData, 0644)
    if err != nil {
       return fmt.Errorf("写入文件失败: %v", err)
    }
    return nil
}

// 从文件读取 JSON 并反序列化为结构体
func readFromFile(filePath string) (Person, error) {
    // 读取文件内容
    data, err := os.ReadFile(filePath)
    if err != nil {
       return Person{}, fmt.Errorf("读取文件失败: %v", err)
    }

    // 将 JSON 字节反序列化为结构体
    var person Person
    err = json.Unmarshal(data, &person)
    if err != nil {
       return Person{}, fmt.Errorf("反序列化 JSON 失败: %v", err)
    }
    return person, nil
}

func main() {
    filePath := "person.json" // 存储 JSON 的文件路径

    // 步骤1创建结构体实例并序列化到文件
    person := Person{
       Name: "张三",
       Age:  30,
    }
    err := writeToFile(person, filePath)
    if err != nil {
       fmt.Println("错误:", err)
       return
    }
    fmt.Println("已将结构体序列化并保存到", filePath)

    // 步骤2从文件读取 JSON 并反序列化为结构体
    loadedPerson, err := readFromFile(filePath)
    if err != nil {
       fmt.Println("错误:", err)
       return
    }
    fmt.Println("从文件读取并反序列化的结果:")
    fmt.Printf("姓名: %s, 年龄: %d\n", loadedPerson.Name, loadedPerson.Age)
}

1. 配置文件示例(config/config.toml

toml

[server]
  port = 8080
  host = "127.0.0.1"

[database]
  link = "mysql:user:password@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4"

[app]
  name = "my-gf-app"
  mode = "dev"  # 多环境标识dev开发、test测试、prod生产

2. 读取配置的代码示例

go

运行

package main

import (
	"context"
	"github.com/gogf/gf/v2/frame/g"
)

func main() {
	ctx := context.Background()
	// 读取简单配置项
	appName := g.Cfg().MustGet(ctx, "app.name").String()
	serverPort := g.Cfg().MustGet(ctx, "server.port").Int()
	dbLink := g.Cfg().MustGet(ctx, "database.link").String()

	g.Log().Info(ctx, "应用名称:", appName)
	g.Log().Info(ctx, "服务端口:", serverPort)
	g.Log().Info(ctx, "数据库链接:", dbLink)
}

三、日志处理(g.Log 配置级别与输出)

日志配置与输出示例

go

运行

package main

import (
	"context"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/log"
)

func main() {
	// 配置日志:同时输出到控制台和文件,级别为 DEBUG
	g.Log().SetConfigWithMap(g.Map{
		"Level": log.LevelDebug, // 日志级别Debug/Info/Warn/Error等
		"Console": g.Map{
			"Enabled": true, // 启用控制台输出
		},
		"File": g.Map{
			"Enabled":  true,  // 启用文件输出
			"Path":     "logs",// 日志存储目录
			"Rotate":   true,  // 按天分割日志
			"RotateBy": "day",
		},
	})

	ctx := context.Background()
	g.Log().Debug(ctx, "这是一条 DEBUG 级别的日志")
	g.Log().Info(ctx, "这是一条 INFO 级别的日志")
	g.Log().Error(ctx, "这是一条 ERROR 级别的日志")
}

四、路由与控制器

1. 路由注册(BindHandler + 控制器注解)

go

运行

package main

import (
	"context"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/net/ghttp"
)

func main() {
	s := g.Server()

	// 方式1直接绑定 Handler
	s.BindHandler("/hello", func(r *ghttp.Request) {
		r.Response.Write("Hello, GoFrame!")
	})

	// 方式2控制器注解定义控制器并绑定方法
	type UserController struct{}
	s.BindHandler("/user/list", (*UserController).List)

	s.Run() // 启动服务,监听 8080 端口
}

// UserController 控制器示例
type UserController struct{}

// List 方法:处理 /user/list 路由,返回 JSON 响应
func (c *UserController) List(r *ghttp.Request) {
	r.Response.WriteJson(g.Map{
		"code": 0,
		"msg":  "success",
		"data": g.Map{"total": 10, "list": []string{"user1", "user2"}},
	})
}

2. 路由参数获取路径、查询、JSON 数据)

go

运行

package main

import (
	"context"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/net/ghttp"
)

func main() {
	s := g.Server()

	// 1. 路径参数(如 /user/123
	s.BindHandler("/user/{id}", func(r *ghttp.Request) {
		id := r.GetInt("id") // 获取路径参数 id
		r.Response.Writef("User ID: %d", id)
	})

	// 2. 查询参数(如 /user?name=john&age=20
	s.BindHandler("/user", func(r *ghttp.Request) {
		name := r.GetString("name") // 查询参数 name
		age := r.GetInt("age")      // 查询参数 age
		r.Response.WriteJson(g.Map{"name": name, "age": age})
	})

	// 3. JSON 数据POST 请求体)
	s.BindHandler("POST:/user", func(r *ghttp.Request) {
		var user struct{ Name string; Age int }
		if err := r.ParseJson(&user); err != nil {
			r.Response.WriteJson(g.Map{"code": 1, "msg": err.Error()})
			return
		}
		r.Response.WriteJson(g.Map{"code": 0, "msg": "success", "data": user})
	})

	s.Run()
}

五、数据库操作ORM + 条件查询 + 事务)

1. CRUD 操作Insert/FindOne/Update/Delete

package main

import (
	"context"
	"github.com/gogf/gf/v2/frame/g"
)

// User 数据模型(对应数据库表 user
type User struct {
	ID   int    `json:"id" gorm:"primaryKey"`
	Name string `json:"name"`
	Age  int    `json:"age"`
}

func main() {
	ctx := context.Background()

	// 1. 插入数据
	user := User{Name: "张三", Age: 30}
	result, err := g.DB().Model("user").Insert(ctx, user)
	if err != nil {
		g.Log().Error(ctx, "插入失败:", err)
		return
	}
	id, _ := result.LastInsertId()
	g.Log().Info(ctx, "插入成功ID:", id)

	// 2. 查询单条数据
	var getOne User
	err = g.DB().Model("user").Where("id", id).FindOne(ctx, &getOne)
	if err != nil {
		g.Log().Error(ctx, "查询失败:", err)
		return
	}
	g.Log().Info(ctx, "查询单条:", getOne)

	// 3. 更新数据
	rows, err := g.DB().Model("user").Where("id", id).Data(g.Map{"age": 31}).Update(ctx)
	if err != nil {
		g.Log().Error(ctx, "更新失败:", err)
		return
	}
	g.Log().Info(ctx, "更新行数:", rows)

	// 4. 删除数据
	rows, err = g.DB().Model("user").Where("id", id).Delete(ctx)
	if err != nil {
		g.Log().Error(ctx, "删除失败:", err)
		return
	}
	g.Log().Info(ctx, "删除行数:", rows)
}

2. 条件查询Where/And/Or + 分页 / 排序)

package main

import (
	"context"
	"github.com/gogf/gf/v2/frame/g"
)

func main() {
	ctx := context.Background()

	// 条件查询 + 分页 + 排序
	var users []g.Map
	err := g.DB().Model("user").
		Where("age > ?", 20).   // 基础条件
		And("name LIKE ?", "张%"). // 且条件
		Order("id DESC").       // 按 ID 倒序
		Limit(10).              // 每页 10 条
		Offset(0).              // 第 1 页(偏移量 0
		Select(ctx, &users)
	if err != nil {
		g.Log().Error(ctx, "查询失败:", err)
		return
	}
	g.Log().Info(ctx, "查询结果:", users)

	// 统计符合条件的总数
	count, err := g.DB().Model("user").Where("age > ?", 20).Count(ctx)
	if err != nil {
		g.Log().Error(ctx, "统计失败:", err)
		return
	}
	g.Log().Info(ctx, "符合条件的总数:", count)
}

3. 事务处理Begin/Commit/Rollback

package main

import (
	"context"
	"github.com/gogf/gf/v2/frame/g"
)

func main() {
	ctx := context.Background()

	// 开启事务
	tx, err := g.DB().Begin(ctx)
	if err != nil {
		g.Log().Error(ctx, "开启事务失败:", err)
		return
	}
	defer tx.Rollback(ctx) // 若未提交,自动回滚

	// 执行 SQL 操作(插入 + 更新)
	_, err = tx.Model("user").Insert(ctx, g.Map{"name": "事务测试", "age": 25})
	if err != nil {
		g.Log().Error(ctx, "插入失败:", err)
		return
	}

	_, err = tx.Model("user").Where("name", "事务测试").Update(ctx, g.Map{"age": 26})
	if err != nil {
		g.Log().Error(ctx, "更新失败:", err)
		return
	}

	// 提交事务
	if err = tx.Commit(ctx); err != nil {
		g.Log().Error(ctx, "提交事务失败:", err)
		return
	}
	g.Log().Info(ctx, "事务执行成功!")
}

D5

  • redis的命令行操作
  • mysql的命令行操作
  • linux的命令操作
  • 学习xuiAdmin

Redis

启动

redis-cli.exe -h 127.0.0.1 -p 6379

设置键值对:

set myKey abc

取出键值对:

get myKey
getrange key st ed #范围取值

127.0.0.1:6379> getrange key 0 1
"aa"


127.0.0.1:6379> mget k1 k2 k3 #取多个值
1) "1"
2) "2"
3) "3"


#哈希的取值
127.0.0.1:6379> hmset hk1 name 1 age 3
OK
127.0.0.1:6379> hmget hk1 name age
1) "1"
2) "3"


#list的操作
127.0.0.1:6379>  LPUSH lkey 2
(integer) 1
127.0.0.1:6379>  LPUSH lkey 4
(integer) 2
127.0.0.1:6379>  LPUSH lkey 2
(integer) 3
127.0.0.1:6379>
127.0.0.1:6379>  LPUSH lkey 25
(integer) 4
127.0.0.1:6379>  lrange lkey
(error) ERR wrong number of arguments for 'lrange' command
127.0.0.1:6379>  lrange lkey 0 10
1) "25"
2) "2"
3) "4"
4) "2"



#set的操作
127.0.0.1:6379> SADD skey 213
(integer) 1
127.0.0.1:6379> SADD skey 2
(integer) 1
127.0.0.1:6379> SADD skey 234
(integer) 1
127.0.0.1:6379> SADD skey 23414
(integer) 1
127.0.0.1:6379> Smembers skey 23414
(error) ERR wrong number of arguments for 'smembers' command
127.0.0.1:6379> Smembers skey
1) "2"
2) "213"
3) "234"
4) "23414"


#Zset的操作
127.0.0.1:6379>  ZADD runoobkey 1 redis
(integer) 1
127.0.0.1:6379>  ZADD runoobkey 2 redis
(integer) 0
127.0.0.1:6379>  ZADD runoobkey 4 redis
(integer) 0
127.0.0.1:6379>  ZADD runoobkey 5 redi
(integer) 1
127.0.0.1:6379>  Zrange runoobkey 0 10
1) "redis"
2) "redi"
127.0.0.1:6379>  ZADD runoobkey 5 rediqwer
(integer) 1
127.0.0.1:6379>  ZADD runoobkey 6 rediqweqweqwr
(integer) 1
127.0.0.1:6379>  Zrange runoobkey 0 10
1) "redis"
2) "redi"
3) "rediqwer"
4) "rediqweqweqwr"
127.0.0.1:6379>exit #退出

linux操作

vi text.txt 进入文件操作

image-20251121090349950

top查看cpu的信息

image-20251121110214172

journalctl 查看日志文件

image-20251121110639891
# 直接查看内容
cat /var/log/myapp.log
# 分页查看
less /var/log/myapp.log
# 实时跟踪日志
tail -f /var/log/myapp.log
image-20251121110905389

free -h 查看内存

image-20251121110950455

df -h 查看磁盘

image-20251121111044708

ifconfig 查看网络

image-20251121111134627

查看TCP/UDP监听端口

ss -tuln

查看80端口的进程

lsof -i :80

image-20251121111237192

image-20251121111250975

压缩

zip 包名 路径

解压

unzip 包名 路径

image-20251121111509765

sodu useradd zds 加用户

sodu passwd zds 改密码

image-20251121111916002

ls -l 查看文件权限

image-20251121112214628

image-20251121112420256

mysql

mysql -u root -p
create database db_1;

image-20251121113731394

mysql> create table db_2.t_1( -> name varchar(50) -> ); 创建表

image-20251121114730228

增加

insert into t_3 values(1,'440111200011111101','Jim','Green');

select * from t_3;

select name,sfz from t_3;

update t_3 set sfz = '440100200010100001';

delete from t_3 where id = 2;

## JWT

  • GenerateToken 生成
  • ParseToken 解析
  • DeleteToken 删除
  • GetAccessToken 获取访问

通过读取config包里面的东西

# JWT配置
jwt:
    signingKey: 39c54195e73304e74a8429b178965865
    expiresTime: 7d
    bufferTime: 1d
    issuer: xiujie # 发行人
    
signingKey用于签名验证的密钥
expiresTimetoken有效期为7天
bufferTime缓冲时间为1天用于提前刷新token
issuer指定token发行方为"xiujie"

登录逻辑

  • 先获取ip

  • 通过LoginParams 封装logininfor

  • 校验验证码 service.SysCaptcha().VerifyCaptcha(ctx, param.CaptchaID, param.CaptchaValue)

  • 获取用户信息 service.SysUser().GetUserByUsernameAndPassword(ctx, param.TenantId, param.Username, param.Password)

  • 构建临时的上下文

  • 生成token

  • 保存登录日志

  • 保存在线列表

失败为1

获取token

从请求头"Authorization"字段或查询参数"access_token"中获取token信息 如果两个地方都为空,则返回错误 优先使用Authorization头中的Bearer token如果为空则使用查询参数中的token

权限

权限码

"cpr:"             // 角色权限码前缀+角色权限
"cpm:"             // 菜单权限码前缀+菜单权限
"cpd:"             // 部门权限码前缀+部门编码
"cpp:"             // 岗位权限码前缀+岗位编码
"cpu:"             // 用户权限码前缀+用户名
"cpc:current:user" // 当前登录用户默认权限

获取服务器信息

IMonitorServer获取所有的信息

mi2ntior.go

	GetGoInfo(ctx context.Context) (res *model.GoRunInfo)
	// GetHostInfo 获取主机信息
	GetHostInfo(ctx context.Context) (data []byte)
	// GetSysLoad 获取系统负载信息
	GetSysLoad(ctx context.Context) (data []byte)
	// GetCpuInfo 获取CPU信息
	GetCpuInfo(ctx context.Context) (data []byte)
	// GetMemInfo 获取内存信息
	GetMemInfo(ctx context.Context) (data []byte)
	// GetDiskInfo 获取磁盘信息
	GetDiskInfo(ctx context.Context) (data []byte)
	// GetNetStatusInfo 获取网络信息
	GetNetStatusInfo(ctx context.Context) (data []byte)

获取电脑信息

var gm runtime.MemStats 定义一个能够获取电脑信息的结构

runtime.ReadMemStats(&gm) 写入

  • 获取主机信息
	hostInfo, _ := host.Info()
	timestamp, _ := host.BootTime()

D6

MODUSBUS

  • Modbus-RTU
  • Modbus-ASCII
  • Modbus-TCP

MODBUS 采用的是主从协议 --> 总线上每次只有一个数据进行传输,即主机发送,从机应答

Modbus-RTU协议

设备必须要有RTU协议这是Modbus协议上规定的且默认模式必须是RTUASCII作为选项

帧结构 = 地址 + 功能码+ 数据 + 校验

  • 地址: 占用一个字节范围0-255其中有效范围是1-247其他有特殊用途比如255是广播地址(广播地址就是应答所有地址,正常的需要两个设备的地址一样才能进行查询和回复)。
  • 功能码:占用一个字节,功能码的意义就是,知道这个指令是干啥的,比如你可以查询从机的数据,也可以修改数据,所以不同功能码对应不同功能。
  • 数据:根据功能码不同,有不同结构,在下面的实例中有说明。
  • 校验:为了保证数据不错误,增加这个,然后再把前面的数据进行计算看数据是否一致,如果一致,就说明这帧数据是正确的,我再回复;如果不一样,说明你这个数据在传输的时候出了问题,数据不对的,所以就抛弃了。

image-20251124102232494

image-20251124102252420

  • 发送:从机的地址+我要干嘛的功能码+我要查的寄存器的地址+我要查的寄存器地址的个数+校验码
  • 回复:从机的地址+主机发我的功能码+要发送给主机数据的字节数+数据+校验码

JWT

  • GenerateToken 生成
  • ParseToken 解析
  • DeleteToken 删除
  • GetAccessToken 获取访问

通过读取config包里面的东西

# JWT配置
jwt:
    signingKey: 39c54195e73304e74a8429b178965865
    expiresTime: 7d
    bufferTime: 1d
    issuer: xiujie # 发行人
    
signingKey用于签名验证的密钥
expiresTimetoken有效期为7天
bufferTime缓冲时间为1天用于提前刷新token
issuer指定token发行方为"xiujie"

登录逻辑

  • 先获取ip

  • 通过LoginParams 封装logininfor

  • 校验验证码 service.SysCaptcha().VerifyCaptcha(ctx, param.CaptchaID, param.CaptchaValue)

  • 获取用户信息 service.SysUser().GetUserByUsernameAndPassword(ctx, param.TenantId, param.Username, param.Password)

  • 构建临时的上下文

  • 生成token

  • 保存登录日志

  • 保存在线列表

失败为1

获取token

从请求头"Authorization"字段或查询参数"access_token"中获取token信息 如果两个地方都为空,则返回错误 优先使用Authorization头中的Bearer token如果为空则使用查询参数中的token

权限

权限码

"cpr:"             // 角色权限码前缀+角色权限
"cpm:"             // 菜单权限码前缀+菜单权限
"cpd:"             // 部门权限码前缀+部门编码
"cpp:"             // 岗位权限码前缀+岗位编码
"cpu:"             // 用户权限码前缀+用户名
"cpc:current:user" // 当前登录用户默认权限

mintior.go

IMonitorServer获取所有的信息

	GetGoInfo(ctx context.Context) (res *model.GoRunInfo)
	// GetHostInfo 获取主机信息
	GetHostInfo(ctx context.Context) (data []byte)
	// GetSysLoad 获取系统负载信息
	GetSysLoad(ctx context.Context) (data []byte)
	// GetCpuInfo 获取CPU信息
	GetCpuInfo(ctx context.Context) (data []byte)
	// GetMemInfo 获取内存信息
	GetMemInfo(ctx context.Context) (data []byte)
	// GetDiskInfo 获取磁盘信息
	GetDiskInfo(ctx context.Context) (data []byte)
	// GetNetStatusInfo 获取网络信息
	GetNetStatusInfo(ctx context.Context) (data []byte)

获取电脑信息

var gm runtime.MemStats 定义一个能够获取电脑信息的结构

runtime.ReadMemStats(&gm) 写入

  • 获取主机信息
	hostInfo, _ := host.Info()
	timestamp, _ := host.BootTime()

1.设备的参数属性从哪里调取?

  • 新加字段 登陆时间的字段 gf dao 一下

  • 所有的写在一个文件像下面

  • uuid32

  • view 返回向 要把新嘉的字段 返回 最后登录时间

  • defer 不用加

  • 建立机构

  • 建立工厂

  • 工厂&机构绑定

  • 用账号登录 设备设置

  • 生产产品

  • 建立订单

  • 设备上来自动连接打印机

11.27

            {
                "deviceId": 166,
                "deviceName": "1127测试设备",
                "deviceSecret": "049c161d",
                    "deviceSn": "00000054",
                "onlineState": 2,
                "lastOnlineTime": "2025-11-27 15:22:18",
                "lastLoginTime": "2025-11-27 15:12:18",
                "svrCode": "hztcp01",
                "remark": "",
                "deviceParams": "",
                "deviceProperties": "{\"properties\":[{\"key\":\"uartId\",\"value\":\"COM3\",\"time\":1764228138210},{\"key\":\"uartState\",\"value\":\"1\",\"time\":1764228138210},{\"key\":\"uartSendLastTime\",\"value\":\"0\",\"time\":1764228138210},{\"key\":\"uartRecvLastTime\",\"value\":\"0\",\"time\":1764228138210},{\"key\":\"startup\",\"value\":\"600\",\"time\":1764228138210}],\"time\":1764228138210}",
                "tenantId": "000000",
                "createdAt": "2025-11-27 15:02:32"
            },
  • gf gen service按 logic 文件的 package 声明分组生成

  • gf gen ctrl按 API 文件的目录结构api/{模块名}/v1/)生成到对应 controller 目录

因此,要让 service 生成到 hzdevelopercenter.go需要确保 logic 文件的包名是 hzdevelopercenter要让 controller 生成到 hzdevelopercenter 目录,需要将 API 文件放在 api/hzdevelopercenter/v1/ 下。

2025-11-28T09:54:17.442+08:00 [DEBU] {c8620c3fd3087c1861902111fe71a759} [ 26 ms] [default] [hz_iot] [rows:1  ] SELECT COUNT(1) FROM `pc_product_test_device` LEFT JOIN `dc_debug_device` ddi ON (ddi.device_sn COLLATE utf8mb4_general_ci = pc_product_test_device.device_sn COLLATE utf8mb4_general_ci) LEFT JOIN `pc_device_info` pdi ON (pdi.device_sn COLLATE utf8mb4_general_ci = ddi.device_sn COLLATE utf8mb4_general_ci) LEFT JOIN `pc_product_info` ppi ON (ppi.product_code COLLATE utf8mb4_general_ci = ddi.product_code COLLATE utf8mb4_general_ci) WHERE ((`pc_product_test_device`.`tenant_id`='000000') AND (`pc_product_test_device`.`device_sn`='00000053')) AND `pdi`.`deleted_at` IS NULL AND `ppi`.`deleted_at` IS NULL
2025-11-28T09:54:17 [DEBU] {c8620c3fd3087c1861902111fe71a759} sMiddleware.ResponseHandler method: GET path: /api/v1/hzdevelopercenter/pcProductTestDevice/list, ================ end ================
package hzdevelopercenter

import (
	"context"
	"xiuadmin/internal/model"
	"xiuadmin/internal/service"

	v1 "xiuadmin/api/hzdevelopercenter/v1"
)

func (c *ControllerV1) PcProductTestDeviceList(ctx context.Context, req *v1.PcProductTestDeviceListReq) (res *v1.PcProductTestDeviceListRes, err error) {
	list, totalCount, err := service.GenPcProductTestDevice().List(ctx, &req.PcProductTestDeviceListParam)
	if err != nil {
		return
	}
	if list == nil {
		list = []*model.PcProductTestDeviceListModel{
			{ProductCode: ""},
		}
	}
	res = new(v1.PcProductTestDeviceListRes)
	//模拟一些数据
	res.Items = list
	res.PageResult.Page = req.Page
	res.PageResult.PageSize = req.PageSize
	res.PageResult.Total = totalCount
	return
}
func (c *ControllerV1) PcProductTestDeviceExport(ctx context.Context, req *v1.PcProductTestDeviceExportReq) (res *v1.PcProductTestDeviceExportRes, err error) {
	err = service.GenPcProductTestDevice().Export(ctx, &req.PcProductTestDeviceListParam)
	return
}
func (c *ControllerV1) PcProductTestDeviceView(ctx context.Context, req *v1.PcProductTestDeviceViewReq) (res *v1.PcProductTestDeviceViewRes, err error) {
	data, err := service.GenPcProductTestDevice().View(ctx, &req.PcProductTestDeviceViewParam)
	if err != nil {
		return
	}

	res = new(v1.PcProductTestDeviceViewRes)
	res.PcProductTestDeviceViewModel = data
	return
}
func (c *ControllerV1) PcProductTestDeviceEdit(ctx context.Context, req *v1.PcProductTestDeviceEditReq) (res *v1.PcProductTestDeviceEditRes, err error) {
	res = new(v1.PcProductTestDeviceEditRes)
	err = service.PcProductTestDevice().Edit(ctx, &req.PcProductTestDeviceEditParam)
	return
}
func (c *ControllerV1) PcProductTestDeviceDelete(ctx context.Context, req *v1.PcProductTestDeviceDeleteReq) (res *v1.PcProductTestDeviceDeleteRes, err error) {
	res = new(v1.PcProductTestDeviceDeleteRes)
	err = service.PcProductTestDevice().Delete(ctx, &req.PcProductTestDeviceDeleteParam)
	return
}