├── .gitignore ├── .gitmodules ├── 1-learn ├── main.go └── summary.md ├── 10-learn ├── LICENSE ├── README.md ├── bear_token.md ├── build_by_docker.md ├── docs │ ├── docs.go │ ├── swagger.json │ └── swagger.yaml ├── enhance_context.md ├── go.mod ├── go.sum ├── hack │ └── proto │ │ ├── rpc_list_user.proto │ │ └── user.proto ├── internal │ ├── common.go │ ├── consts │ │ └── error_msg.go │ ├── dao │ │ └── couchdb.go │ ├── enhance_context.go │ ├── entity │ │ ├── album.go │ │ ├── base.go │ │ └── user.go │ ├── errors │ │ └── biz_error.go │ ├── gateway │ │ ├── album │ │ │ ├── create_one.go │ │ │ ├── delete_by_id.go │ │ │ ├── get_by_id.go │ │ │ └── list.go │ │ ├── authorize │ │ │ ├── login.go │ │ │ └── register.go │ │ ├── authorize_valid.go │ │ └── engine.go │ ├── logic │ │ ├── album │ │ │ └── album_service_impl.go │ │ ├── authorize │ │ │ ├── authorize_service_impl.go │ │ │ └── sub_imple.go │ │ └── logic.go │ ├── service │ │ ├── ialbum_service.go │ │ └── iauthorize_service.go │ └── util │ │ ├── config.go │ │ ├── copy.go │ │ └── token │ │ ├── inspect.go │ │ ├── jwt_impl.go │ │ ├── paseto_impl.go │ │ ├── payload.go │ │ └── token_helper.go ├── main.go ├── manifest │ └── app.env ├── use_gmssl.md ├── use_swagger.md └── wechat_2024-11-05_151938_084.png ├── 2-learn ├── 1-test.go ├── 2-test.go ├── 3-test.go ├── 4-test.go ├── struct.md └── summary.md ├── 3-learn ├── 1-test.go ├── 2-test.go ├── 3-test.go ├── 4-test.go ├── 5-test.go └── summary.md ├── 4-learn ├── Main.go ├── fs │ ├── Error.go │ ├── FormatJSON.go │ ├── Fs.go │ ├── ReadFile.go │ └── WriteFile.go └── summary.md ├── 5-learn ├── DBUtil │ ├── ConnectionInfo.go │ ├── Error.go │ ├── GetConn.go │ ├── Insert.go │ ├── Query.go │ └── Update.go ├── Main.go ├── day05.sql └── summary.md ├── 6-learn ├── Main.go ├── simpleService │ └── SimpleService.go ├── simpleService2 │ └── SimpleService.go └── summary.md ├── 7-learn └── index.md ├── 8-learn ├── App_test.go ├── Http_test.go └── summary.md ├── 9-learn ├── CppGrpcClient │ ├── CMakeLists.txt │ ├── Makefile │ └── src │ │ ├── main.cpp │ │ ├── proto │ │ ├── .gitkeep │ │ └── user_service.proto │ │ ├── user_service.grpc.pb.cc │ │ ├── user_service.grpc.pb.h │ │ ├── user_service.pb.cc │ │ └── user_service.pb.h ├── Makefile ├── build_cpp_grpc_client.md ├── go.mod ├── go.sum ├── main.go ├── pb │ ├── .gitkeep │ ├── user_service.pb.go │ └── user_service_grpc.pb.go ├── proto │ └── user_service.proto └── summary.md ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── SUMMARY.md └── officialGuide ├── 1-tour.go ├── 10-tour.go ├── 11-tour.go ├── 12-tour.go ├── 13-tour.go ├── 14-tour.go ├── 15-tour.go ├── 16-tour.go ├── 17-tour.go ├── 18-tour.go ├── 19-tour.go ├── 2-tour.go ├── 20-tour.go ├── 21-tour.go ├── 22-tour.go ├── 23-tour.go ├── 24-tour.go ├── 25-tour.go ├── 26-tour.go ├── 27-tour.go ├── 3-tour.go ├── 4-tour.go ├── 5-tour.go ├── 6-tour.go ├── 7-tour.go ├── 8-tour.go ├── 9-tour.go └── summary.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.dll 4 | *.so 5 | *.dylib 6 | .idea 7 | _book/ 8 | pkg 9 | # Test binary, build with `go test -c` 10 | *.test 11 | 12 | # Output of the go coverage tool, specifically when used with LiteIDE 13 | *.out 14 | 15 | # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 16 | .glide/ 17 | day08/html/js/lib 18 | day08/html/stylesheets/lib 19 | day08/html/images/ 20 | 21 | # cmake 22 | **/build/* 23 | **/cmake-build-debug/* 24 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "day08-project"] 2 | path = day08-project 3 | url = https://github.com/afterloe/awpaas-manager 4 | [submodule "day09-project"] 5 | path = day09-project 6 | url = https://github.com/afterloe/awpaas-route 7 | -------------------------------------------------------------------------------- /1-learn/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "errors" 6 | ) 7 | 8 | const name = "赵小鹏" // 申明一个常量 9 | 10 | /* 11 | Go语言有一些默认的行为。 12 | ● 大写字母开头的变量是可导出的,即其他包可以读取,是公用变量;小写字母开头的不可导出,是私有变量。 13 | ● 大写字母开头的函数也是一样,相当于class中带public关键词的公有函数;小写字母开头就是有private关键词的私有函数。 14 | */ 15 | func main() { 16 | var age = 2 // 申明一个变量 17 | fmt.Printf("Hello, world or %s \nage is %d \n", name, age) // 调用一个系统库 - 输出流 18 | 19 | var isActive = true // bool 默认值是 false 20 | fmt.Printf("isActive value default is %d \n", isActive) 21 | 22 | var a, b = 12, 24 23 | c := a + b 24 | fmt.Printf("12 + 24 = %d \n", c) 25 | 26 | d, e := 11.3, 23.4 27 | f := d * e 28 | fmt.Printf("11.3 * 23.4 = %.2f \n", f) 29 | 30 | s1 := "java" 31 | arr_byt_s1 := []byte(s1) // 将字符串转换为 byte 数组 32 | arr_byt_s1[0] = 'n' // 修改第一项为 n 33 | s2 := string(arr_byt_s1) // 再转换成 字符串 34 | fmt.Printf("修改字符串 %s\n", s2) 35 | 36 | s := `这是一个 37 | 多行的文本` 38 | fmt.Printf("%s \n", s) 39 | 40 | s1 = "b" + s1[2:] // 支持字符串切割 41 | fmt.Printf("s1内容是 %s \r\n", s1) 42 | 43 | err := errors.New("emit macho dwarf: elf header corrupted") 44 | if err != nil { 45 | fmt.Print(err) 46 | fmt.Println() 47 | } 48 | 49 | /* 50 | Go 支持多个常量、变量、导入多个包 写法如下 51 | 52 | import ("fmt", "os") 53 | const (i=199, pi=3.1415, prefix="Go_") 54 | vat (i int, pi float32, prefix string) 55 | */ 56 | 57 | const ( 58 | x = iota // 0 59 | y = iota // 1 60 | z = iota // 2 61 | w) // 3 62 | 63 | fmt.Printf("%d \n", w) 64 | 65 | var arr [10]int // 指定数据 66 | arr[0] = 43 67 | arr[1] = 12 68 | 69 | fmt.Printf("the first element is %d \n", arr[0]) 70 | 71 | arr1 := [3]int {1, 2, 3} // 带有数据的初始化 72 | fmt.Printf("the first element is %d \n", arr1[2]) 73 | 74 | // 动态数组slice 75 | arr2 := [...]int {4, 5, 6} // 省略长度,自动计算 76 | fmt.Printf("the first element is %d \n", arr2[0]) 77 | 78 | // 二维数组 79 | doubleArr := [2][4]int {{1,2,3,4}, {6,7,8,9}} 80 | fmt.Printf("the 1*3 的内容是 %d \n", doubleArr[1][3]) 81 | 82 | // 动态数组 slice 83 | arr3 := [...]byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'} 84 | arr4 := [...]int{1, 2, 3, 4, 5, 6, 7, 8} 85 | var ( 86 | aSlice = arr3[:3] 87 | bSlice = arr3[4:5] 88 | ) 89 | 90 | // 数组切割 91 | fmt.Println(arr3[3:]) 92 | fmt.Println(arr4[4:]) 93 | fmt.Println(arr4[2:4]) 94 | 95 | // append函数会改变slice所引用的数组的内容,从而影响到引用同一数组的其他slice。 96 | fmt.Printf("aSlice first element is %b, bSlice first element is %b \n", aSlice[0], bSlice[0]) 97 | 98 | /** 99 | map是无序的 100 | map的长度是不固定的 101 | */ 102 | //var numberMap map[string] int // 两种申明方式 103 | numberMap := make(map[string]int) // make用于内建类型(map、slice和channel)的内存分配。new用于各种类型的内存分配。 104 | // make只能创建slice、map和channel,并且返回一个有初始值(非零)的T 类型 make返回初始化后的(非零)值。 105 | numberMap["one"] = 1 // 赋值 106 | fmt.Printf("element is %d", numberMap["one"]) // 读取数据 107 | 108 | numberMap = map[string]int {"c": 4, "d": 23} // 直接赋值 109 | numberMap["two"] = 2 110 | _, ok := numberMap["one"] // 判断 这个 map 是否包含 one 111 | if ok { 112 | // map内置有判断是否存在key的方式,通过delete删除map的元素 113 | fmt.Println("one is ") 114 | } else { 115 | // map也是一种引用类型,如果两个map同时指向一个底层,那么一个改变,另一个也相应改变 116 | fmt.Printf("no have one . map len is %d \r\n", len(numberMap)) 117 | } 118 | // 两种写法 119 | if val, ok := numberMap["two"]; ok { 120 | fmt.Printf("two number is -> %d \r\n", val) 121 | } 122 | 123 | /* 124 | 各类型初始值 125 | int 0 126 | int8 0 127 | int32 0 128 | int64 0 129 | uint 0x0 130 | rune 0 131 | byte 0x0 132 | float32 0 133 | float64 0 134 | boolean false 135 | string "" 136 | */ 137 | } 138 | -------------------------------------------------------------------------------- /1-learn/summary.md: -------------------------------------------------------------------------------- 1 | # 第一天 2 | > 代码链接 [./main.go](./main.go) 3 | 4 | ## 写在前面的话 5 | 如果只是运行可以使用`go run main.go` 来执行,不用build。 6 | 7 | ## go工程的目录结构 8 | ```sbtshell 9 | GOPATH 10 | --- bin # 存放编译后的相关可执行文件 11 | --- pkg # 平台相关目录 12 | --- src # 源码 13 | ``` 14 | 15 | > 查找源码的方式 16 | ```shell 17 | $ which go 18 | /usr/bin/go 19 | $ cd /usr/bin 20 | $ ls -las | grep go 21 | 0 lrwxrwxrwx 1 root root 16 Sep 17 14:48 go -> ../lib/go/bin/go 22 | $ cd ../lib/go 23 | $ ls 24 | api CONTRIBUTING.md favicon.ico misc README.md src 25 | AUTHORS CONTRIBUTORS lib PATENTS robots.txt test 26 | bin doc LICENSE pkg SECURITY.md VERSION 27 | $ cd src 28 | ``` 29 | 30 | ## GO 中的潜规则 31 | 32 | Go语言有一些默认的行为。 33 | - 大写字母开头的变量是可导出的,即其他包可以读取,是公用变量;小写字母开头的不可导出,是私有变量。 34 | - 大写字母开头的函数也是一样,相当于class中带public关键词的公有函数;小写字母开头就是有private关键词的私有函数。 35 | 36 | ## 变量 37 | 38 | 变量分为 `var` 和 `const`。 申明变量可以有已下几种方式 39 | ```golang 40 | var age = 2 41 | c := 2 42 | 43 | var ( 44 | a = 1, 45 | b = 2 46 | ) 47 | ``` 48 | 49 | `:=` 是一种简写,它和`var`不能一同使用, 第二一个是可以进行批量赋值。 50 | 51 | ## map 和 slice 52 | 类似于 `HashMap` 和 `LinkedArrayList` 都有自己的默认方法。 53 | 54 | ## 数组的切割可以使用如下的方式 55 | 56 | ```golang 57 | a := [...]int{1, 2, 3, 4, 5, 6, 7, 8} 58 | a[4:] // 跳过前4个item, 并返回后面的 [5, 6, 7, 8] 59 | a[3:4] // 从第4个item开始, 截止到第5个item,返回中间元素的数组[4] 60 | a[2:4] // 从第3个item开始, 截止到第五个item,返回中间元素的数组[3, 4] 61 | ``` 62 | 63 | ## 强制转换 64 | string -> int 65 | * `strconv.Atoi("1")` # 自动转换 66 | * `strconv.ParseInt("1", 10, 64)` # 把十进制的1 转为int64类型 67 | 68 | int -> string 69 | * `strconv.Itoa(1)` 70 | * `strconv.FormatInt(int64, 1)` 71 | 72 | ## 各类型的初始变量 73 | 74 | - make用于内建类型(map、slice和channel)的内存分配。new用于各种类型的内存分配,直接返回指针`*T` 75 | - make只能创建slice、map和channel,并且返回一个有初始值(非零)的T 类型 make返回初始化后的(非零)值。 76 | 77 | 类型 | 值 78 | ---- | ---- 79 | int | 0 80 | int8 | 0 81 | int32 | 0 82 | int64 | 0 83 | uint | 0x0 84 | rune | 0 85 | byte | 0x0 86 | float32 | 0 87 | float64 | 0 88 | boolean | false 89 | string | "" 90 | -------------------------------------------------------------------------------- /10-learn/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 john@goframe.org https://goframe.org 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /10-learn/README.md: -------------------------------------------------------------------------------- 1 | 项目1 - 简单的相册管理 2 | === 3 | > create by [afterloe](liumin@hengzhiinfo.cn) 4 | > version is 1.0.15 5 | > MIT LICENSE 6 | 7 | ## 项目目录结构设计 8 | | 目录/文件名称 | 说明 | 描述 | 9 | |------------|------|----------------------------------------------------| 10 | | api | 对外接口 | 对外提供服务的输入/输出数据结构定义。考虑到版本管理需要,往往以api/xxx/v1...存在。 | 11 | | hack | 工具脚本 | 存放项目开发工具、脚本等内容。例如,CLI工具的配置,各种shell/bat脚本等文件。 | 12 | | internal | 内部逻辑 | 业务逻辑存放目录。通过Golang internal特性对外部隐藏可见性。 | 13 | | - cmd | 入口指令 | 命令行管理目录。可以管理维护多个命令行。 | 14 | | - consts | 常量定义 | 项目所有常量定义。 | 15 | | - gateway | 接口处理 | 接收/解析用户输入参数的入口/接口层。 | 16 | | - dao | 数据访问 | 数据访问对象,这是一层抽象对象,用于和底层数据库交互,仅包含最基础的 CURD 方法 | 17 | | - logic | 业务封装 | 业务逻辑封装管理,特定的业务逻辑实现和封装。往往是项目中最复杂的部分。 | 18 | | - entity | 结构模型 | 数据结构管理模块,管理数据实体对象,以及输入与输出数据结构定义。 | 19 | | - service | 业务接口 | 用于业务模块解耦的接口定义层。具体的接口实现在logic中进行注入。 | 20 | | - util | 通用工具 | 用于内部调用的工具集合 | 21 | | manifest | 交付清单 | 包含程序编译、部署、运行、配置的文件。常见内容如下: | 22 | | - config | 配置管理 | 配置文件存放目录。 | 23 | | - docker | 镜像文件 | Docker镜像相关依赖文件,脚本文件等等。 | 24 | | - deploy | 部署文件 | 部署相关的文件。默认提供了Kubernetes集群化部署的Yaml模板,通过kustomize管理。 | 25 | | - protobuf | 协议文件 | GRPC协议时使用的protobuf协议定义文件,协议文件编译后生成go文件到api目录。 | 26 | | resource | 静态资源 | 静态资源文件。这些文件往往可以通过 资源打包/镜像编译 的形式注入到发布文件中。 | 27 | | go.mod | 依赖管理 | 使用Go Module包管理的依赖描述文件。 | 28 | | main.go | 入口文件 | 程序入口文件。 | 29 | 30 | ## 数据库 31 | CouchDB 32 | ```shell 33 | docker run -d \ 34 | -p 5984:5984 \ 35 | --name my-couchdb \ 36 | -e COUCHDB_USER=admin \ 37 | -e COUCHDB_PASSWORD=111111hZ! \ 38 | apache/couchdb:latest 39 | ``` 40 | 41 | ## 相关资料 42 | 43 | * [增强Context, gin封装web服务](enhance_context.md) 44 | * [国密算法集成](use_gmssl.md) 45 | * [jwt Token](bear_token.md) 46 | * [Swagger 配置](use_swagger.md) 47 | * [构建通用Go打包镜像](build_by_docker.md) -------------------------------------------------------------------------------- /10-learn/bear_token.md: -------------------------------------------------------------------------------- 1 | go中如何实现bear token拦截和生成 2 | === 3 | 4 | 关于jwt token,这里一块可以自行去百度,这里使用的是两个库 5 | jwt [github.com/golang-jwt/jwt/v5](https://github.com/golang-jwt/jwt) 6 | passto [github.com/o1egl/paseto](https://github.com/o1egl/paseto) 7 | 8 | ## 实践过程 9 | 10 | ### 构建通用token工具 11 | 设计一个Token需要存储信息的对象,全部代码可参考[internal/util/token](internal/util/token)包内, 部分代码如下: 12 | ```go 13 | package token 14 | 15 | import ( 16 | "time" 17 | ) 18 | 19 | // Payload token中存储的信息 20 | type Payload struct { 21 | ID string `json:"id"` // 用户id 22 | Username string `json:"username"` // 用户名 23 | Role []string `json:"role"` // 用户角色列表 24 | IssuedAt time.Time `json:"issued_at"` // token创建时间 25 | ExpiredAt time.Time `json:"expired_at"` // token过期时间 26 | } 27 | ``` 28 | 代码参考: [internal/util/token/payload.go](internal/util/token/payload.go) 29 | 30 | 基于这个对象,构建TokenHelper工具和接口,以及实现方式,若只有一种则自行去掉多余的内容 31 | ```go 32 | package token 33 | 34 | import ( 35 | "errors" 36 | "time" 37 | ) 38 | 39 | // token类型,是access_token 还是 refresh_token 40 | const ( 41 | ACCESS = "access" 42 | REFRESH = "refresh" 43 | ) 44 | 45 | // ITokenHelper tokenHelper接口 46 | type iTokenHelper interface { 47 | // CreateToken 48 | // 为登陆用户创建token 49 | // @Param id string 用户id 50 | // @Param username string 用户名 51 | // @Param role []string 用户角色列表 52 | // @Param tokenType string token类型 access, refresh 53 | // @Return token, token.Payload, 异常 54 | CreateToken(id, username string, role []string, tokenType string) (string, *Payload, error) 55 | 56 | // VerifyToken 57 | // 验证Token字符串是否有效 58 | // @Param token string token字符串 59 | // @Return token.Payload,异常 60 | VerifyToken(token string) (*Payload, error) 61 | 62 | // SetDurationTime 63 | // 设置token存在时间 64 | // @Param accessTokenDuration 访问token存在时间 65 | // @Param refreshTokenDuration 刷新token存在时间 66 | SetDurationTime(accessTokenDuration, refreshTokenDuration time.Duration) 67 | } 68 | 69 | // ValidExpired 70 | // 验证token是否超时 71 | // Param expired 设置的时间 72 | // Return error 未超时返回nil 73 | func ValidExpired(expired time.Time) error { 74 | if time.Now().After(expired) { 75 | return errors.New("token 已过期") 76 | } 77 | return nil 78 | } 79 | 80 | // generatorPayload 81 | // 生成token封装信息 82 | // Param id string 用户id 83 | // Param username string 用户名 84 | // Param role []string 用户角色列表 85 | // Param duration time.Duration token存在时间 86 | // Return token.Payload 87 | func generatorPayload(id, username string, role []string, duration time.Duration) *Payload { 88 | return &Payload{ 89 | ID: id, 90 | Username: username, 91 | Role: role, 92 | IssuedAt: time.Now(), 93 | ExpiredAt: time.Now().Add(duration), 94 | } 95 | } 96 | ``` 97 | 代码参考: [internal/util/token/token_helper.go](internal/util/token/token_helper.go) 98 | #### JWT 实现 99 | ```go 100 | func (that *jwtImpl) CreateToken(id, username string, role []string, tokenType string) (string, *Payload, error) { 101 | var duration time.Duration 102 | if tokenType == ACCESS { 103 | duration = that.accessTokenDuration 104 | } else if tokenType == REFRESH { 105 | duration = that.refreshTokenDuration 106 | } 107 | payload := generatorPayload(id, username, role, duration) 108 | claims := jwt.MapClaims{} 109 | claims["id"] = payload.ID 110 | claims["username"] = payload.Username 111 | claims["role"] = payload.Role 112 | claims["issued_at"] = payload.IssuedAt 113 | claims["expired_at"] = payload.ExpiredAt 114 | token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) 115 | tokenStr, err := token.SignedString(that.symmetricKey) 116 | return tokenStr, payload, err 117 | } 118 | ``` 119 | 具体代码实现可参考[internal/util/token/jwt_impl.go](internal/util/token/jwt_impl.go)。 120 | 121 | #### Passto实现 122 | ```go 123 | func (that *pasetoImpl) CreateToken(id, username string, role []string, tokenType string) (string, *Payload, error) { 124 | var duration time.Duration 125 | if tokenType == ACCESS { 126 | duration = that.accessTokenDuration 127 | } else if tokenType == REFRESH { 128 | duration = that.refreshTokenDuration 129 | } 130 | payload := generatorPayload(id, username, role, duration) 131 | token, err := that.paseto.Encrypt(that.symmetricKey, payload, nil) 132 | return token, payload, err 133 | } 134 | ``` 135 | 具体代码实现可参考[internal/util/token/paseto_impl.go](internal/util/token/paseto_impl.go) 136 | 137 | ### tokenHelper实例化 138 | ```go 139 | package token 140 | 141 | var Inspect iTokenHelper // tokenHelper 实例 142 | 143 | // NewTokenMaker 构建TokenHelper实例 144 | func NewTokenMaker(symmetricKey, tokenType string) { 145 | if "paseto" == tokenType { 146 | Inspect = newPase2Inspect([]byte(symmetricKey)) 147 | } else { 148 | Inspect = newJWTInspect([]byte(symmetricKey)) 149 | } 150 | } 151 | ``` 152 | 代码参考: [internal/util/token/inspect.go](internal/util/token/inspect.go) 153 | 154 | 这样tokenHelper工具就支持jwt和passto两种方式。 155 | 156 | ### 工具使用 157 | 158 | #### 初始化 159 | ```go 160 | func runTokenKeyInit(config util.Config) { 161 | token.NewTokenMaker(config.TokenSymmetricKey, config.TokenType) 162 | token.Inspect.SetDurationTime(config.AccessTokenDuration, config.RefreshTokenDuration) 163 | } 164 | ``` 165 | 从配置文件中读取是实现jwt还是passto,同时配置AccessToken和RefreshToken两种token的存在时间. 初始化是在[main.go](main.go)中`78`行实现。 166 | 167 | #### 登陆成功后生成Token 168 | ```go 169 | func (*_authorizeServiceImpl) Login(loginName, scrip string) (string, string, error) { 170 | ciphertext := _str2Ciphertext(scrip) 171 | query := fmt.Sprintf(`{"selector":{"$and":[{"username":"%s"},{"ciphertext":"%s"}]},"fields":["_id", "username"]}`, loginName, ciphertext) 172 | result := dao.Couchdb.Find(table, query) 173 | if !result.Next() { 174 | return "", "", &errors.BizError{ 175 | ErrorCode: 401, 176 | Msg: consts.LoginNameOrPassword, 177 | } 178 | } 179 | var user entity.User 180 | _ = result.ScanDoc(&user) 181 | // 查询到用户信息后 调用 tokenHelper进行token创建 182 | accessToken, _, err := token.Inspect.CreateToken(user.Id, user.Username, []string{}, token.ACCESS) 183 | if err != nil { 184 | return "", "", err 185 | } 186 | refreshToken, _, err := token.Inspect.CreateToken(user.Id, user.Username, []string{}, token.REFRESH) 187 | if err != nil { 188 | return "", "", err 189 | } 190 | return accessToken, refreshToken, err 191 | } 192 | ``` 193 | 该段代码在[internal/logic/authorize/authorize_service_impl.go](internal/logic/authorize/authorize_service_impl.go)的第`22`行。 194 | 195 | #### 在路由时对请求进行拦截 196 | 这边使用的是Gin的中间件实现 197 | ```go 198 | package gateway 199 | 200 | import ( 201 | "github.com/gin-gonic/gin" 202 | "net/http" 203 | "onenet/internal" 204 | "onenet/internal/util/token" 205 | "strings" 206 | ) 207 | 208 | func AuthorizeValid() gin.HandlerFunc { 209 | return func(c *gin.Context) { 210 | // 增强Context 211 | ctx := &internal.Context{Context: c} 212 | 213 | // 得到请求头 214 | bearerToken := ctx.GetHeader("Authorization") 215 | 216 | // 若不是 Bear xx 格式则报错 217 | if len(strings.Split(bearerToken, " ")) != 2 { 218 | ctx.IndentedJSON(http.StatusUnauthorized, &internal.CommonResponse[interface{}]{ 219 | BizCode: http.StatusUnauthorized, 220 | Error: "未授权", 221 | }) 222 | ctx.Abort() 223 | return 224 | } 225 | // 获取token字符串 226 | tokenStr := strings.Split(bearerToken, " ")[1] 227 | 228 | // 验证token字符串是否合法 229 | payload, err := token.Inspect.VerifyToken(tokenStr) 230 | if err != nil { 231 | ctx.IndentedJSON(http.StatusUnauthorized, &internal.CommonResponse[interface{}]{ 232 | BizCode: http.StatusUnauthorized, 233 | Error: "未授权", 234 | }) 235 | ctx.Abort() 236 | return 237 | } 238 | 239 | // 验证token字符串是否超时 240 | err = token.ValidExpired(payload.ExpiredAt) 241 | if err != nil { 242 | ctx.IndentedJSON(http.StatusUnauthorized, &internal.CommonResponse[interface{}]{ 243 | BizCode: http.StatusUnauthorized, 244 | Error: "登陆超时", 245 | }) 246 | ctx.Abort() 247 | return 248 | } 249 | ctx.Set("authorize", payload) 250 | ctx.Next() 251 | } 252 | } 253 | ``` 254 | 该段代码在[internal/gateway/authorize_valid.go](internal/gateway/authorize_valid.go) 255 | 256 | 被用于[internal/gateway/engine.go](internal/gateway/engine.go)的`94`行 257 | ```go 258 | authorizeRoute := routes.Group("/").Use(AuthorizeValid()) 259 | authorizeRoute.POST("/album/list", album.List) 260 | authorizeRoute.POST("/album/get_by_id", album.GetById) 261 | ``` -------------------------------------------------------------------------------- /10-learn/build_by_docker.md: -------------------------------------------------------------------------------- 1 | 使用docker构建通用go打包镜像 2 | === 3 | 使用go进行打包的步骤如下 4 | ## 使用docker镜像构建可运行对象 5 | ```shell 6 | docker run -it -v .:/go -v .:/app --rm golang:1.23.2 go build -v 7 | docker run -it -v .:/ --rm alpine 8 | ``` 9 | 或使用如下dockerfile进行构建 10 | ```dockerfile 11 | FROM golang:1.13 as builder 12 | 13 | WORKDIR /app 14 | COPY . . 15 | RUN go build -o timelocation . 16 | 17 | FROM alpine:latest 18 | 19 | WORKDIR /app 20 | COPY --from=builder /app/timelocation /app 21 | 22 | ENTRYPOINT ["./timelocation"] 23 | ``` 24 | 25 | ```makefile 26 | # create by afterloe 27 | 28 | .PHONY: build,compile,structure 29 | SHELL := /bin/bash 30 | WORKDIR = $(shell pwd) 31 | BUILD_IMG = awpaas/builder 32 | VERSION = $(shell more package.json | grep version | awk -F '"' 'NR==1{print$$4}') 33 | NAME = $(shell more package.json | grep name | awk -F '"' 'NR==1{print$$4}') 34 | BUILD_ENV = $(shell docker image ls | grep ${BUILD_IMG} | wc -l) 35 | 36 | all: compile structure 37 | 38 | .ONESHELL: 39 | compile: $(src) package.json 40 | docker run -it -v $(src):/go/src -v $(WORKDIR):/app --rm ${BUILD_IMG}:1.0.0 go build -v 41 | structure: app 42 | docker build -t awpaas/$(NAME):$(VERSION) . 43 | ``` 44 | 45 | ```dockerfile 46 | FROM alpine:3.8 47 | MAINTAINER afterloe 48 | 49 | ENV \ 50 | PROJECT_DIR="/app" 51 | WORKDIR ${PROJECT_DIR} 52 | COPY app ${PROJECT_DIR} 53 | COPY package.json ${PROJECT_DIR} 54 | EXPOSE 8080 8081 55 | CMD ./app 56 | ``` 57 | 58 | ### 构建go相关项目 59 | 使用如下脚本进行编译 60 | ```shell 61 | #!/bin/bash 62 | echo "ready to build image" 63 | src=$1 64 | out=$2 65 | echo "use ${src} to build" 66 | echo "bin export to ${out}" 67 | docker run -it \ 68 | -v ${src}:/go/src \ 69 | -v ${out}:/app \ 70 | --rm \ 71 | awpaas/builder:1.0.0 \ 72 | go build 73 | ``` 74 | > 使用方法`sh build.sh ./src ./dist` 75 | 76 | ### awpaas系列组件中使用make来构建 77 | ```sbtshell 78 | make -m src=/data/data-2/go/src 79 | docker run -it awpaas/awpaas-route:1.0.0 80 | ``` 81 | ### build 82 | ```sbtshell 83 | docker build -t awpaas/builder:1.0.0 . 84 | ``` -------------------------------------------------------------------------------- /10-learn/docs/swagger.json: -------------------------------------------------------------------------------- 1 | { 2 | "swagger": "2.0", 3 | "info": { 4 | "contact": {} 5 | }, 6 | "paths": { 7 | "/album/create_one": { 8 | "post": { 9 | "description": "创建相册", 10 | "consumes": [ 11 | "application/json" 12 | ], 13 | "produces": [ 14 | "application/json" 15 | ], 16 | "summary": "创建相册", 17 | "parameters": [ 18 | { 19 | "description": "创建相册所需要的参数", 20 | "name": "json", 21 | "in": "body", 22 | "required": true, 23 | "schema": { 24 | "$ref": "#/definitions/album.CreateAlbumRequest" 25 | } 26 | } 27 | ], 28 | "responses": { 29 | "200": { 30 | "description": "请求成功", 31 | "schema": { 32 | "$ref": "#/definitions/internal.CommonResponse-album_CreateAlbumResponse" 33 | } 34 | } 35 | } 36 | } 37 | }, 38 | "/album/list": { 39 | "post": { 40 | "description": "列表查询所有相册", 41 | "consumes": [ 42 | "application/json" 43 | ], 44 | "produces": [ 45 | "application/json" 46 | ], 47 | "summary": "列表查询所有相册", 48 | "parameters": [ 49 | { 50 | "description": "分页参数", 51 | "name": "json", 52 | "in": "body", 53 | "required": true, 54 | "schema": { 55 | "$ref": "#/definitions/album.ListRequest" 56 | } 57 | } 58 | ], 59 | "responses": { 60 | "200": { 61 | "description": "请求成功", 62 | "schema": { 63 | "$ref": "#/definitions/internal.CommonResponse-album_ListResponse" 64 | } 65 | } 66 | } 67 | } 68 | }, 69 | "/authorize/login": { 70 | "post": { 71 | "description": "登陆", 72 | "consumes": [ 73 | "application/json" 74 | ], 75 | "produces": [ 76 | "application/json" 77 | ], 78 | "summary": "登陆", 79 | "parameters": [ 80 | { 81 | "description": "登陆时所需要的信息", 82 | "name": "json", 83 | "in": "body", 84 | "required": true, 85 | "schema": { 86 | "$ref": "#/definitions/authorize.LoginRequest" 87 | } 88 | } 89 | ], 90 | "responses": { 91 | "200": { 92 | "description": "token信息", 93 | "schema": { 94 | "$ref": "#/definitions/internal.CommonResponse-authorize_LoginResponse" 95 | } 96 | } 97 | } 98 | } 99 | }, 100 | "/authorize/register": { 101 | "post": { 102 | "description": "注册用户", 103 | "consumes": [ 104 | "application/json" 105 | ], 106 | "produces": [ 107 | "application/json" 108 | ], 109 | "summary": "注册用户", 110 | "parameters": [ 111 | { 112 | "description": "注册用户所需的信息", 113 | "name": "json", 114 | "in": "body", 115 | "required": true, 116 | "schema": { 117 | "$ref": "#/definitions/authorize.RegisterRequest" 118 | } 119 | } 120 | ], 121 | "responses": { 122 | "200": { 123 | "description": "请求成功", 124 | "schema": { 125 | "$ref": "#/definitions/internal.CommonResponse-string" 126 | } 127 | } 128 | } 129 | } 130 | } 131 | }, 132 | "definitions": { 133 | "album.CreateAlbumRequest": { 134 | "type": "object", 135 | "required": [ 136 | "title" 137 | ], 138 | "properties": { 139 | "description": { 140 | "description": "描述", 141 | "type": "string" 142 | }, 143 | "summary": { 144 | "description": "概况", 145 | "type": "string" 146 | }, 147 | "title": { 148 | "description": "相册标题", 149 | "type": "string" 150 | } 151 | } 152 | }, 153 | "album.CreateAlbumResponse": { 154 | "type": "object", 155 | "required": [ 156 | "title" 157 | ], 158 | "properties": { 159 | "description": { 160 | "description": "描述", 161 | "type": "string" 162 | }, 163 | "id": { 164 | "type": "string" 165 | }, 166 | "summary": { 167 | "description": "概况", 168 | "type": "string" 169 | }, 170 | "title": { 171 | "description": "相册标题", 172 | "type": "string" 173 | } 174 | } 175 | }, 176 | "album.ListRequest": { 177 | "type": "object", 178 | "properties": { 179 | "content": { 180 | "type": "string" 181 | }, 182 | "page": { 183 | "description": "第几页", 184 | "type": "integer", 185 | "default": 1 186 | }, 187 | "size": { 188 | "description": "每页多少条", 189 | "type": "integer", 190 | "default": 10 191 | } 192 | } 193 | }, 194 | "album.ListResponse": { 195 | "type": "object", 196 | "properties": { 197 | "records": { 198 | "type": "array", 199 | "items": { 200 | "$ref": "#/definitions/entity.Album" 201 | } 202 | }, 203 | "total": { 204 | "type": "integer" 205 | } 206 | } 207 | }, 208 | "authorize.LoginRequest": { 209 | "type": "object", 210 | "required": [ 211 | "login_name", 212 | "scrip" 213 | ], 214 | "properties": { 215 | "login_name": { 216 | "description": "用户名", 217 | "type": "string" 218 | }, 219 | "scrip": { 220 | "description": "编码", 221 | "type": "string" 222 | } 223 | } 224 | }, 225 | "authorize.LoginResponse": { 226 | "type": "object", 227 | "properties": { 228 | "access_token": { 229 | "type": "string" 230 | }, 231 | "refresh_token": { 232 | "type": "string" 233 | }, 234 | "session_id": { 235 | "type": "string" 236 | }, 237 | "user": { 238 | "type": "string" 239 | } 240 | } 241 | }, 242 | "authorize.RegisterRequest": { 243 | "type": "object", 244 | "required": [ 245 | "password", 246 | "username" 247 | ], 248 | "properties": { 249 | "password": { 250 | "description": "密码", 251 | "type": "string" 252 | }, 253 | "username": { 254 | "description": "用户名", 255 | "type": "string" 256 | } 257 | } 258 | }, 259 | "entity.Album": { 260 | "type": "object", 261 | "properties": { 262 | "_id": { 263 | "type": "string" 264 | }, 265 | "artist": { 266 | "description": "创建人名字", 267 | "type": "string" 268 | }, 269 | "created_at": { 270 | "description": "创建时间", 271 | "type": "string" 272 | }, 273 | "created_by": { 274 | "description": "创建人", 275 | "type": "string" 276 | }, 277 | "deleted": { 278 | "description": "是否逻辑删除", 279 | "type": "boolean" 280 | }, 281 | "description": { 282 | "description": "描述", 283 | "type": "string" 284 | }, 285 | "first_pic": { 286 | "description": "第一张照片", 287 | "allOf": [ 288 | { 289 | "$ref": "#/definitions/entity.Picture" 290 | } 291 | ] 292 | }, 293 | "id": { 294 | "type": "string" 295 | }, 296 | "last_view_at": { 297 | "description": "上次查看日期", 298 | "type": "string" 299 | }, 300 | "modified_at": { 301 | "description": "修改时间", 302 | "type": "string" 303 | }, 304 | "modified_by": { 305 | "description": "修改人", 306 | "type": "string" 307 | }, 308 | "order_index": { 309 | "description": "排序,升序", 310 | "type": "integer" 311 | }, 312 | "price": { 313 | "description": "照片数量", 314 | "type": "integer" 315 | }, 316 | "summary": { 317 | "description": "概况", 318 | "type": "string" 319 | }, 320 | "title": { 321 | "description": "相册标题", 322 | "type": "string" 323 | } 324 | } 325 | }, 326 | "entity.Picture": { 327 | "type": "object", 328 | "properties": { 329 | "_id": { 330 | "type": "string" 331 | }, 332 | "abs_path": { 333 | "description": "相对路径", 334 | "type": "string" 335 | }, 336 | "created_at": { 337 | "description": "创建时间", 338 | "type": "string" 339 | }, 340 | "created_by": { 341 | "description": "创建人", 342 | "type": "string" 343 | }, 344 | "deleted": { 345 | "description": "是否逻辑删除", 346 | "type": "boolean" 347 | }, 348 | "file_name": { 349 | "description": "文件名", 350 | "type": "string" 351 | }, 352 | "hidden": { 353 | "description": "是否隐藏", 354 | "type": "boolean" 355 | }, 356 | "id": { 357 | "type": "string" 358 | }, 359 | "location": { 360 | "description": "拍摄位置", 361 | "type": "string" 362 | }, 363 | "modified_at": { 364 | "description": "修改时间", 365 | "type": "string" 366 | }, 367 | "modified_by": { 368 | "description": "修改人", 369 | "type": "string" 370 | }, 371 | "real_path": { 372 | "description": "真实路径", 373 | "type": "string" 374 | }, 375 | "size": { 376 | "description": "照片大小,字节", 377 | "type": "integer" 378 | }, 379 | "suffix": { 380 | "description": "后缀名", 381 | "type": "string" 382 | }, 383 | "tags": { 384 | "description": "标签", 385 | "type": "array", 386 | "items": { 387 | "type": "string" 388 | } 389 | } 390 | } 391 | }, 392 | "internal.CommonResponse-album_CreateAlbumResponse": { 393 | "type": "object", 394 | "properties": { 395 | "biz_code": { 396 | "description": "业务编码,0 - 正常", 397 | "type": "integer" 398 | }, 399 | "error": { 400 | "description": "异常信息,正确请求时为空白", 401 | "type": "string" 402 | }, 403 | "result": { 404 | "description": "返回的数据", 405 | "allOf": [ 406 | { 407 | "$ref": "#/definitions/album.CreateAlbumResponse" 408 | } 409 | ] 410 | } 411 | } 412 | }, 413 | "internal.CommonResponse-album_ListResponse": { 414 | "type": "object", 415 | "properties": { 416 | "biz_code": { 417 | "description": "业务编码,0 - 正常", 418 | "type": "integer" 419 | }, 420 | "error": { 421 | "description": "异常信息,正确请求时为空白", 422 | "type": "string" 423 | }, 424 | "result": { 425 | "description": "返回的数据", 426 | "allOf": [ 427 | { 428 | "$ref": "#/definitions/album.ListResponse" 429 | } 430 | ] 431 | } 432 | } 433 | }, 434 | "internal.CommonResponse-authorize_LoginResponse": { 435 | "type": "object", 436 | "properties": { 437 | "biz_code": { 438 | "description": "业务编码,0 - 正常", 439 | "type": "integer" 440 | }, 441 | "error": { 442 | "description": "异常信息,正确请求时为空白", 443 | "type": "string" 444 | }, 445 | "result": { 446 | "description": "返回的数据", 447 | "allOf": [ 448 | { 449 | "$ref": "#/definitions/authorize.LoginResponse" 450 | } 451 | ] 452 | } 453 | } 454 | }, 455 | "internal.CommonResponse-string": { 456 | "type": "object", 457 | "properties": { 458 | "biz_code": { 459 | "description": "业务编码,0 - 正常", 460 | "type": "integer" 461 | }, 462 | "error": { 463 | "description": "异常信息,正确请求时为空白", 464 | "type": "string" 465 | }, 466 | "result": { 467 | "description": "返回的数据", 468 | "type": "string" 469 | } 470 | } 471 | } 472 | } 473 | } -------------------------------------------------------------------------------- /10-learn/docs/swagger.yaml: -------------------------------------------------------------------------------- 1 | definitions: 2 | album.CreateAlbumRequest: 3 | properties: 4 | description: 5 | description: 描述 6 | type: string 7 | summary: 8 | description: 概况 9 | type: string 10 | title: 11 | description: 相册标题 12 | type: string 13 | required: 14 | - title 15 | type: object 16 | album.CreateAlbumResponse: 17 | properties: 18 | description: 19 | description: 描述 20 | type: string 21 | id: 22 | type: string 23 | summary: 24 | description: 概况 25 | type: string 26 | title: 27 | description: 相册标题 28 | type: string 29 | required: 30 | - title 31 | type: object 32 | album.ListRequest: 33 | properties: 34 | content: 35 | type: string 36 | page: 37 | default: 1 38 | description: 第几页 39 | type: integer 40 | size: 41 | default: 10 42 | description: 每页多少条 43 | type: integer 44 | type: object 45 | album.ListResponse: 46 | properties: 47 | records: 48 | items: 49 | $ref: '#/definitions/entity.Album' 50 | type: array 51 | total: 52 | type: integer 53 | type: object 54 | authorize.LoginRequest: 55 | properties: 56 | login_name: 57 | description: 用户名 58 | type: string 59 | scrip: 60 | description: 编码 61 | type: string 62 | required: 63 | - login_name 64 | - scrip 65 | type: object 66 | authorize.LoginResponse: 67 | properties: 68 | access_token: 69 | type: string 70 | refresh_token: 71 | type: string 72 | session_id: 73 | type: string 74 | user: 75 | type: string 76 | type: object 77 | authorize.RegisterRequest: 78 | properties: 79 | password: 80 | description: 密码 81 | type: string 82 | username: 83 | description: 用户名 84 | type: string 85 | required: 86 | - password 87 | - username 88 | type: object 89 | entity.Album: 90 | properties: 91 | _id: 92 | type: string 93 | artist: 94 | description: 创建人名字 95 | type: string 96 | created_at: 97 | description: 创建时间 98 | type: string 99 | created_by: 100 | description: 创建人 101 | type: string 102 | deleted: 103 | description: 是否逻辑删除 104 | type: boolean 105 | description: 106 | description: 描述 107 | type: string 108 | first_pic: 109 | allOf: 110 | - $ref: '#/definitions/entity.Picture' 111 | description: 第一张照片 112 | id: 113 | type: string 114 | last_view_at: 115 | description: 上次查看日期 116 | type: string 117 | modified_at: 118 | description: 修改时间 119 | type: string 120 | modified_by: 121 | description: 修改人 122 | type: string 123 | order_index: 124 | description: 排序,升序 125 | type: integer 126 | price: 127 | description: 照片数量 128 | type: integer 129 | summary: 130 | description: 概况 131 | type: string 132 | title: 133 | description: 相册标题 134 | type: string 135 | type: object 136 | entity.Picture: 137 | properties: 138 | _id: 139 | type: string 140 | abs_path: 141 | description: 相对路径 142 | type: string 143 | created_at: 144 | description: 创建时间 145 | type: string 146 | created_by: 147 | description: 创建人 148 | type: string 149 | deleted: 150 | description: 是否逻辑删除 151 | type: boolean 152 | file_name: 153 | description: 文件名 154 | type: string 155 | hidden: 156 | description: 是否隐藏 157 | type: boolean 158 | id: 159 | type: string 160 | location: 161 | description: 拍摄位置 162 | type: string 163 | modified_at: 164 | description: 修改时间 165 | type: string 166 | modified_by: 167 | description: 修改人 168 | type: string 169 | real_path: 170 | description: 真实路径 171 | type: string 172 | size: 173 | description: 照片大小,字节 174 | type: integer 175 | suffix: 176 | description: 后缀名 177 | type: string 178 | tags: 179 | description: 标签 180 | items: 181 | type: string 182 | type: array 183 | type: object 184 | internal.CommonResponse-album_CreateAlbumResponse: 185 | properties: 186 | biz_code: 187 | description: 业务编码,0 - 正常 188 | type: integer 189 | error: 190 | description: 异常信息,正确请求时为空白 191 | type: string 192 | result: 193 | allOf: 194 | - $ref: '#/definitions/album.CreateAlbumResponse' 195 | description: 返回的数据 196 | type: object 197 | internal.CommonResponse-album_ListResponse: 198 | properties: 199 | biz_code: 200 | description: 业务编码,0 - 正常 201 | type: integer 202 | error: 203 | description: 异常信息,正确请求时为空白 204 | type: string 205 | result: 206 | allOf: 207 | - $ref: '#/definitions/album.ListResponse' 208 | description: 返回的数据 209 | type: object 210 | internal.CommonResponse-authorize_LoginResponse: 211 | properties: 212 | biz_code: 213 | description: 业务编码,0 - 正常 214 | type: integer 215 | error: 216 | description: 异常信息,正确请求时为空白 217 | type: string 218 | result: 219 | allOf: 220 | - $ref: '#/definitions/authorize.LoginResponse' 221 | description: 返回的数据 222 | type: object 223 | internal.CommonResponse-string: 224 | properties: 225 | biz_code: 226 | description: 业务编码,0 - 正常 227 | type: integer 228 | error: 229 | description: 异常信息,正确请求时为空白 230 | type: string 231 | result: 232 | description: 返回的数据 233 | type: string 234 | type: object 235 | info: 236 | contact: {} 237 | paths: 238 | /album/create_one: 239 | post: 240 | consumes: 241 | - application/json 242 | description: 创建相册 243 | parameters: 244 | - description: 创建相册所需要的参数 245 | in: body 246 | name: json 247 | required: true 248 | schema: 249 | $ref: '#/definitions/album.CreateAlbumRequest' 250 | produces: 251 | - application/json 252 | responses: 253 | "200": 254 | description: 请求成功 255 | schema: 256 | $ref: '#/definitions/internal.CommonResponse-album_CreateAlbumResponse' 257 | summary: 创建相册 258 | /album/list: 259 | post: 260 | consumes: 261 | - application/json 262 | description: 列表查询所有相册 263 | parameters: 264 | - description: 分页参数 265 | in: body 266 | name: json 267 | required: true 268 | schema: 269 | $ref: '#/definitions/album.ListRequest' 270 | produces: 271 | - application/json 272 | responses: 273 | "200": 274 | description: 请求成功 275 | schema: 276 | $ref: '#/definitions/internal.CommonResponse-album_ListResponse' 277 | summary: 列表查询所有相册 278 | /authorize/login: 279 | post: 280 | consumes: 281 | - application/json 282 | description: 登陆 283 | parameters: 284 | - description: 登陆时所需要的信息 285 | in: body 286 | name: json 287 | required: true 288 | schema: 289 | $ref: '#/definitions/authorize.LoginRequest' 290 | produces: 291 | - application/json 292 | responses: 293 | "200": 294 | description: token信息 295 | schema: 296 | $ref: '#/definitions/internal.CommonResponse-authorize_LoginResponse' 297 | summary: 登陆 298 | /authorize/register: 299 | post: 300 | consumes: 301 | - application/json 302 | description: 注册用户 303 | parameters: 304 | - description: 注册用户所需的信息 305 | in: body 306 | name: json 307 | required: true 308 | schema: 309 | $ref: '#/definitions/authorize.RegisterRequest' 310 | produces: 311 | - application/json 312 | responses: 313 | "200": 314 | description: 请求成功 315 | schema: 316 | $ref: '#/definitions/internal.CommonResponse-string' 317 | summary: 注册用户 318 | swagger: "2.0" 319 | -------------------------------------------------------------------------------- /10-learn/enhance_context.md: -------------------------------------------------------------------------------- 1 | Gin封装 2 | === 3 | 代码及调用过程可参考[main.go](main.go),[enhance_context](internal/enhance_context.go), [internal/gateway](internal/gateway) 4 | ## 前言 5 | 6 | 以往对Gin的加强是在创建一个中间键进行,代码如下 7 | ``` 8 | *gin.Engine.Use(handle HandlerFunc) 9 | ``` 10 | 当多个中间件存在的时候会产生一系列无法预估的bug,这里提供一种基于Gin的封装,将对应的方法加在Context上,以减少对中间件的依赖,同时亦能保留Gin的原始能力 11 | 12 | ## 实践过程 13 | ### 编写自定义Context 14 | ```golang 15 | // Context 封装的Context 16 | type Context struct { 17 | *gin.Context 18 | } 19 | 20 | // HandlerFunc 路由接收的默认参数 21 | type HandlerFunc func(c *Context) 22 | 23 | // EnhanceContext 增强Context防范 24 | func EnhanceContext(handler HandlerFunc) gin.HandlerFunc { 25 | return func(c *gin.Context) { 26 | handler(&Context{c}) 27 | } 28 | } 29 | ``` 30 | 定义路由处理方法 31 | ```go 32 | func Login(ctx *internal.Context) { 33 | // do something 34 | } 35 | ``` 36 | 37 | ### 编写增强方法 38 | ```go 39 | // Success 请求成功 40 | func (that *Context) Success(data ...interface{}) { 41 | var r interface{} 42 | if len(data) > 0 { 43 | r = data[0] 44 | } 45 | that.IndentedJSON(http.StatusOK, &CommonResponse[interface{}]{ 46 | BizCode: 0, 47 | Result: r, 48 | }) 49 | that.Abort() // 中断链 50 | } 51 | 52 | // FailWithStr 请求失败 string 53 | func (that *Context) FailWithStr(str string) { 54 | that.IndentedJSON(http.StatusOK, &CommonResponse[interface{}]{ 55 | Error: str, 56 | BizCode: http.StatusInternalServerError, 57 | }) 58 | that.Abort() // 中断链 59 | } 60 | ``` 61 | 62 | ### 封装 63 | ```golang 64 | package main 65 | 66 | import ( 67 | "github.com/gin-gonic/gin" 68 | "net/http" 69 | "onenet/internal/util" 70 | ) 71 | 72 | type ( 73 | RouterGroup struct { 74 | gin.RouterGroup 75 | } 76 | 77 | Engine struct { 78 | Config util.Config 79 | Background *http.Server 80 | *gin.Engine 81 | } 82 | ) 83 | ``` 84 | gin的核心能力为engine和RouterGroup,这两类会对Context有一定依赖,故对此进行封装。 85 | 对以下方法进行重写 86 | ```go 87 | func (that RouterGroup) POST(relativePath string, handlers ...internal.HandlerFunc) RouterGroup { 88 | that.RouterGroup.POST(relativePath, internal.EnhanceContext(handlers[0])) 89 | return that 90 | } 91 | 92 | func (that RouterGroup) Group(relativePath string, handlers ...internal.HandlerFunc) RouterGroup { 93 | if len(handlers) != 0 { 94 | that.RouterGroup.Group(relativePath, internal.EnhanceContext(handlers[0])) 95 | } else { 96 | that.RouterGroup.Group(relativePath) 97 | } 98 | return that 99 | } 100 | 101 | func (that RouterGroup) Use(handlers ...gin.HandlerFunc) RouterGroup { 102 | that.RouterGroup.Use(handlers...) 103 | return that 104 | } 105 | ``` 106 | 这三个方法在路由绑定的时候用的频率最高,重写这三个方法让其返回我们自己定义的Context 107 | 108 | ### 调用 109 | ```go 110 | func (that *Engine) _bind() { 111 | that.Use(gin.Logger()) 112 | that.Use(gin.Recovery()) 113 | 114 | routes := &RouterGroup{ 115 | that.RouterGroup, 116 | } 117 | routes.POST("/authorize/register", authorize.Register) 118 | routes.POST("/authorize/login", authorize.Login) 119 | 120 | authorizeRoute := routes.Group("/").Use(AuthorizeValid()) 121 | authorizeRoute.POST("/album/list", album.List) 122 | authorizeRoute.POST("/album/get_by_id", album.GetById) 123 | authorizeRoute.POST("/album/create_one", album.CreateOne) 124 | authorizeRoute.POST("/album/delete_by_id", album.DeleteById) 125 | } 126 | ``` 127 | 中间件仍可以使用 Gin提供的,同时也能在路由测使用增强的Context。 -------------------------------------------------------------------------------- /10-learn/go.mod: -------------------------------------------------------------------------------- 1 | module onenet 2 | 3 | go 1.23.2 4 | 5 | require ( 6 | github.com/GmSSL/GmSSL-Go v1.3.1 7 | github.com/gin-contrib/cors v1.7.2 8 | github.com/gin-gonic/gin v1.10.0 9 | github.com/go-kivik/kivik/v4 v4.3.2 10 | github.com/golang-jwt/jwt/v5 v5.2.1 11 | github.com/google/uuid v1.6.0 12 | github.com/o1egl/paseto v1.0.0 13 | github.com/spf13/viper v1.19.0 14 | github.com/swaggo/files v1.0.1 15 | github.com/swaggo/gin-swagger v1.6.0 16 | github.com/swaggo/swag v1.16.4 17 | golang.org/x/sync v0.8.0 18 | ) 19 | 20 | require ( 21 | github.com/KyleBanks/depth v1.2.1 // indirect 22 | github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect 23 | github.com/aead/chacha20poly1305 v0.0.0-20170617001512-233f39982aeb // indirect 24 | github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 // indirect 25 | github.com/bytedance/sonic v1.12.3 // indirect 26 | github.com/bytedance/sonic/loader v0.2.0 // indirect 27 | github.com/cloudwego/base64x v0.1.4 // indirect 28 | github.com/cloudwego/iasm v0.2.0 // indirect 29 | github.com/fsnotify/fsnotify v1.7.0 // indirect 30 | github.com/gabriel-vasile/mimetype v1.4.6 // indirect 31 | github.com/gin-contrib/sse v0.1.0 // indirect 32 | github.com/go-openapi/jsonpointer v0.21.0 // indirect 33 | github.com/go-openapi/jsonreference v0.21.0 // indirect 34 | github.com/go-openapi/spec v0.21.0 // indirect 35 | github.com/go-openapi/swag v0.23.0 // indirect 36 | github.com/go-playground/locales v0.14.1 // indirect 37 | github.com/go-playground/universal-translator v0.18.1 // indirect 38 | github.com/go-playground/validator/v10 v10.22.1 // indirect 39 | github.com/goccy/go-json v0.10.3 // indirect 40 | github.com/hashicorp/hcl v1.0.0 // indirect 41 | github.com/josharian/intern v1.0.0 // indirect 42 | github.com/json-iterator/go v1.1.12 // indirect 43 | github.com/klauspost/cpuid/v2 v2.2.8 // indirect 44 | github.com/leodido/go-urn v1.4.0 // indirect 45 | github.com/magiconair/properties v1.8.7 // indirect 46 | github.com/mailru/easyjson v0.7.7 // indirect 47 | github.com/mattn/go-isatty v0.0.20 // indirect 48 | github.com/mitchellh/mapstructure v1.5.0 // indirect 49 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 50 | github.com/modern-go/reflect2 v1.0.2 // indirect 51 | github.com/pelletier/go-toml/v2 v2.2.3 // indirect 52 | github.com/pkg/errors v0.9.1 // indirect 53 | github.com/sagikazarmark/locafero v0.4.0 // indirect 54 | github.com/sagikazarmark/slog-shim v0.1.0 // indirect 55 | github.com/sourcegraph/conc v0.3.0 // indirect 56 | github.com/spf13/afero v1.11.0 // indirect 57 | github.com/spf13/cast v1.6.0 // indirect 58 | github.com/spf13/pflag v1.0.5 // indirect 59 | github.com/subosito/gotenv v1.6.0 // indirect 60 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect 61 | github.com/ugorji/go/codec v1.2.12 // indirect 62 | go.uber.org/atomic v1.9.0 // indirect 63 | go.uber.org/multierr v1.9.0 // indirect 64 | golang.org/x/arch v0.11.0 // indirect 65 | golang.org/x/crypto v0.28.0 // indirect 66 | golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect 67 | golang.org/x/net v0.30.0 // indirect 68 | golang.org/x/sys v0.26.0 // indirect 69 | golang.org/x/text v0.19.0 // indirect 70 | golang.org/x/tools v0.26.0 // indirect 71 | google.golang.org/protobuf v1.35.1 // indirect 72 | gopkg.in/ini.v1 v1.67.0 // indirect 73 | gopkg.in/yaml.v3 v3.0.1 // indirect 74 | ) 75 | -------------------------------------------------------------------------------- /10-learn/hack/proto/rpc_list_user.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package middleware; 4 | 5 | import "user.proto"; 6 | 7 | option go_package = "onenet/middleware"; 8 | 9 | message ListUserReq { 10 | int32 page = 1; 11 | int32 size = 2; 12 | string content = 3; 13 | } 14 | 15 | message ListUserRes { 16 | User user = 1; 17 | } -------------------------------------------------------------------------------- /10-learn/hack/proto/user.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package middleware; 4 | 5 | import "google/protobuf/timestamp.proto"; 6 | 7 | option go_package = "onenet/middleware"; 8 | 9 | message User { 10 | string username = 1; 11 | string full_name = 2; 12 | string email = 3; 13 | google.protobuf.Timestamp created_at = 5; 14 | } -------------------------------------------------------------------------------- /10-learn/internal/common.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | // PageParam 分页查询 4 | type PageParam struct { 5 | Page int `default:"1" json:"page"` // 第几页 6 | Size int `default:"10" json:"size"` // 每页多少条 7 | } 8 | 9 | // CommonResponse 通用返回格式 10 | type CommonResponse[T any] struct { 11 | BizCode int `json:"biz_code"` // 业务编码,0 - 正常 12 | Error string `json:"error"` // 异常信息,正确请求时为空白 13 | Result T `json:"result"` // 返回的数据 14 | } 15 | -------------------------------------------------------------------------------- /10-learn/internal/consts/error_msg.go: -------------------------------------------------------------------------------- 1 | package consts 2 | 3 | const ( 4 | ReadyRegister = "用户%s已经被注册" 5 | LoginNameOrPassword = "用户名或密码错误" 6 | ) 7 | -------------------------------------------------------------------------------- /10-learn/internal/dao/couchdb.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/go-kivik/kivik/v4" 7 | _ "github.com/go-kivik/kivik/v4/couchdb" 8 | "github.com/google/uuid" 9 | "log" 10 | "onenet/internal/util" 11 | "reflect" 12 | "time" 13 | ) 14 | 15 | var ( 16 | driverName = "couch" 17 | Couchdb *_couchDB 18 | ) 19 | 20 | type _couchDB struct { 21 | protocol string 22 | username string 23 | password string 24 | url string 25 | dbName string 26 | client *kivik.Client 27 | db *kivik.DB 28 | failCount int 29 | } 30 | 31 | func Register(config util.Config) { 32 | Couchdb = &_couchDB{ 33 | protocol: "http", 34 | username: config.DBUser, 35 | password: config.DBPassword, 36 | url: config.DBUrl, 37 | dbName: config.DBName, 38 | failCount: 0, 39 | } 40 | } 41 | 42 | func (that *_couchDB) generatorURL() string { 43 | return fmt.Sprintf("%s://%s:%s@%s/", that.protocol, that.username, that.password, that.url) 44 | } 45 | 46 | func (that *_couchDB) Find(dbName string, query interface{}, options ...kivik.Option) *kivik.ResultSet { 47 | db := that.GetDB(dbName) 48 | return db.Find(context.TODO(), query, options...) 49 | } 50 | 51 | func (that *_couchDB) Put(dbName string, entity interface{}, options ...kivik.Option) (string, error) { 52 | db := that.GetDB(dbName) 53 | id, err := uuid.NewUUID() 54 | if err != nil { 55 | return "", err 56 | } 57 | idStr := id.String() 58 | reflectValue := reflect.ValueOf(entity) 59 | reflectValue.Elem().FieldByName("Id").SetString(idStr) 60 | //reflectValue.Elem().FieldByName("CreatedBy").SetString() 61 | reflectValue.Elem().FieldByName("CreatedAt").Set(reflect.ValueOf(time.Now())) 62 | reflectValue.Elem().FieldByName("Deleted").SetBool(false) 63 | rev, err := db.Put(context.TODO(), idStr, entity, options...) 64 | if err != nil { 65 | return "", err 66 | } 67 | log.Printf("album inserted with revision %s", rev) 68 | return idStr, nil 69 | } 70 | 71 | func (that *_couchDB) GetDB(dbNames ...string) *kivik.DB { 72 | client := that.GetClient() 73 | if len(dbNames) == 0 { 74 | return client.DB(that.dbName) 75 | } else { 76 | return client.DB(dbNames[0]) 77 | } 78 | } 79 | 80 | func (that *_couchDB) GetClient() *kivik.Client { 81 | register: 82 | if that.client == nil { 83 | client, err := kivik.New(driverName, that.generatorURL()) 84 | if err != nil { 85 | log.Fatal(err) 86 | } 87 | that.client = client 88 | } 89 | 90 | if flag, err := that.client.Ping(context.TODO()); nil != err || flag == false { 91 | log.Println("client is close") 92 | goto register 93 | } 94 | 95 | return that.client 96 | } 97 | -------------------------------------------------------------------------------- /10-learn/internal/enhance_context.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "errors" 5 | "github.com/gin-gonic/gin" 6 | "net/http" 7 | bizError "onenet/internal/errors" 8 | "onenet/internal/util/token" 9 | "reflect" 10 | ) 11 | 12 | // Context 封装的Context 13 | type Context struct { 14 | *gin.Context 15 | } 16 | 17 | // HandlerFunc 路由接收的默认参数 18 | type HandlerFunc func(c *Context) 19 | 20 | // EnhanceContext 增强Context方法 21 | func EnhanceContext(handler HandlerFunc) gin.HandlerFunc { 22 | return func(c *gin.Context) { 23 | handler(&Context{c}) 24 | } 25 | } 26 | 27 | // BindAuthorizeInfo 为entity._base 中 绑定创建人、修改人id 28 | func (that *Context) BindAuthorizeInfo(entity interface{}) { 29 | info := that.AuthorizeInfo() 30 | reflectValue := reflect.ValueOf(entity) 31 | reflectValue.Elem().FieldByName("CreatedBy").SetString(info.ID) 32 | reflectValue.Elem().FieldByName("ModifiedBy").SetString(info.ID) 33 | } 34 | 35 | // AuthorizeInfo 获取登陆人信息 36 | func (that *Context) AuthorizeInfo() *token.Payload { 37 | val, ok := that.Get("authorize") 38 | if ok { 39 | return val.(*token.Payload) 40 | } 41 | return nil 42 | } 43 | 44 | // Success 请求成功 45 | func (that *Context) Success(data ...interface{}) { 46 | var r interface{} 47 | if len(data) > 0 { 48 | r = data[0] 49 | } 50 | that.IndentedJSON(http.StatusOK, &CommonResponse[interface{}]{ 51 | BizCode: 0, 52 | Result: r, 53 | }) 54 | that.Abort() 55 | } 56 | 57 | // FailWithStr 请求失败 string 58 | func (that *Context) FailWithStr(str string) { 59 | that.IndentedJSON(http.StatusOK, &CommonResponse[interface{}]{ 60 | Error: str, 61 | BizCode: http.StatusInternalServerError, 62 | }) 63 | that.Abort() 64 | } 65 | 66 | // Fail 请求失败 error 67 | func (that *Context) Fail(err error) { 68 | response := &CommonResponse[interface{}]{ 69 | Error: err.Error(), 70 | } 71 | var h *bizError.BizError 72 | if errors.As(err, &h) { 73 | response.BizCode = h.ErrorCode 74 | } else { 75 | response.BizCode = http.StatusInternalServerError 76 | } 77 | that.IndentedJSON(http.StatusOK, response) 78 | that.Abort() 79 | } 80 | -------------------------------------------------------------------------------- /10-learn/internal/entity/album.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | // Album 4 | // 相册 5 | type Album struct { 6 | ID string `json:"id"` 7 | Title string `json:"title"` // 相册标题 8 | Artist string `json:"artist"` // 创建人名字 9 | Summary string `json:"summary"` // 概况 10 | Description string `json:"description"` // 描述 11 | Number int `json:"price"` // 照片数量 12 | OrderIndex int `json:"order_index"` // 排序,升序 13 | FirstPic *Picture `json:"first_pic"` // 第一张照片 14 | LastViewAt string `json:"last_view_at"` // 上次查看日期 15 | _base 16 | } 17 | 18 | // Picture 19 | // 相片 20 | type Picture struct { 21 | ID string `json:"id"` 22 | FileName string `json:"file_name"` // 文件名 23 | Suffix string `json:"suffix"` // 后缀名 24 | Size int64 `json:"size"` // 照片大小,字节 25 | Hidden bool `json:"hidden"` // 是否隐藏 26 | AbsPath string `json:"abs_path"` // 相对路径 27 | Tags []string `json:"tags"` // 标签 28 | Location string `json:"location"` // 拍摄位置 29 | RealPath string `json:"real_path"` // 真实路径 30 | _base 31 | } 32 | -------------------------------------------------------------------------------- /10-learn/internal/entity/base.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import "time" 4 | 5 | // _base 6 | // 基础数据类 7 | type _base struct { 8 | Id string `json:"_id"` 9 | CreatedAt time.Time `json:"created_at"` // 创建时间 10 | CreatedBy string `json:"created_by"` // 创建人 11 | ModifiedAt time.Time `json:"modified_at"` // 修改时间 12 | ModifiedBy string `json:"modified_by"` // 修改人 13 | Deleted bool `json:"deleted"` // 是否逻辑删除 14 | } 15 | -------------------------------------------------------------------------------- /10-learn/internal/entity/user.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | // User 4 | // 用户 5 | type User struct { 6 | ID string `json:"id"` 7 | Username string `json:"username"` 8 | Ciphertext string `json:"ciphertext"` 9 | _base 10 | } 11 | -------------------------------------------------------------------------------- /10-learn/internal/errors/biz_error.go: -------------------------------------------------------------------------------- 1 | package errors 2 | 3 | type BizError struct { 4 | ErrorCode int `json:"error_code"` 5 | Msg string `json:"msg"` 6 | } 7 | 8 | func (e *BizError) Error() string { 9 | return e.Msg 10 | } 11 | -------------------------------------------------------------------------------- /10-learn/internal/gateway/album/create_one.go: -------------------------------------------------------------------------------- 1 | package album 2 | 3 | import ( 4 | "onenet/internal" 5 | "onenet/internal/entity" 6 | "onenet/internal/service" 7 | "onenet/internal/util" 8 | ) 9 | 10 | type ( 11 | CreateAlbumRequest struct { 12 | Title string `json:"title" binding:"required"` // 相册标题 13 | Summary string `json:"summary"` // 概况 14 | Description string `json:"description"` // 描述 15 | } 16 | 17 | CreateAlbumResponse struct { 18 | ID string `json:"id"` 19 | CreateAlbumRequest 20 | } 21 | ) 22 | 23 | // CreateOne 24 | // @BasePath /album/create_one 25 | // @Summary 创建相册 26 | // @Description 创建相册 27 | // @Accept json 28 | // @Produce json 29 | // @Param json body album.CreateAlbumRequest true "创建相册所需要的参数" 30 | // @Success 200 {object} internal.CommonResponse[album.CreateAlbumResponse] "请求成功" 31 | // @Router /album/create_one [POST] 32 | func CreateOne(ctx *internal.Context) { 33 | var request CreateAlbumRequest 34 | err := ctx.ShouldBind(&request) 35 | if err != nil { 36 | ctx.FailWithStr("绑定失败") 37 | ctx.Fail(err) 38 | return 39 | } 40 | 41 | album := entity.Album{} 42 | err = util.Copy(&album, request) 43 | if err != nil { 44 | ctx.Fail(err) 45 | return 46 | } 47 | ctx.BindAuthorizeInfo(&album) 48 | info := ctx.AuthorizeInfo() 49 | album.Artist = info.Username 50 | r, err := service.AlbumService().CreateOne(&album) 51 | if err != nil { 52 | ctx.Fail(err) 53 | return 54 | } 55 | 56 | res := CreateAlbumResponse{} 57 | _ = util.Copy(&res, r) 58 | res.ID = r.Id 59 | ctx.Success(res) 60 | } 61 | -------------------------------------------------------------------------------- /10-learn/internal/gateway/album/delete_by_id.go: -------------------------------------------------------------------------------- 1 | package album 2 | 3 | import ( 4 | "onenet/internal" 5 | ) 6 | 7 | func DeleteById(ctx *internal.Context) { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /10-learn/internal/gateway/album/get_by_id.go: -------------------------------------------------------------------------------- 1 | package album 2 | 3 | import ( 4 | "onenet/internal" 5 | ) 6 | 7 | func GetById(ctx *internal.Context) { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /10-learn/internal/gateway/album/list.go: -------------------------------------------------------------------------------- 1 | package album 2 | 3 | import ( 4 | "onenet/internal" 5 | "onenet/internal/entity" 6 | "onenet/internal/service" 7 | ) 8 | 9 | // List 10 | // @BasePath /album/list 11 | // @Summary 列表查询所有相册 12 | // @Description 列表查询所有相册 13 | // @Accept json 14 | // @Produce json 15 | // @Param json body album.ListRequest true "分页参数" 16 | // @Success 200 {object} internal.CommonResponse[album.ListResponse] "请求成功" 17 | // @Router /album/list [POST] 18 | func List(ctx *internal.Context) { 19 | var req ListRequest 20 | err := ctx.Bind(&req) 21 | if err != nil { 22 | ctx.FailWithStr("绑定失败") 23 | } 24 | records, total, err := service.AlbumService().ListByPage(req.Content, req.Page, req.Size) 25 | if err != nil { 26 | ctx.Fail(err) 27 | return 28 | } 29 | ctx.Success(&ListResponse{ 30 | Total: total, 31 | Records: records, 32 | }) 33 | } 34 | 35 | type ( 36 | ListRequest struct { 37 | Content string `json:"content"` 38 | internal.PageParam 39 | } 40 | 41 | ListResponse struct { 42 | Total int `json:"total"` 43 | Records []*entity.Album `json:"records"` 44 | } 45 | ) 46 | -------------------------------------------------------------------------------- /10-learn/internal/gateway/authorize/login.go: -------------------------------------------------------------------------------- 1 | package authorize 2 | 3 | import ( 4 | "onenet/internal" 5 | "onenet/internal/service" 6 | ) 7 | 8 | type ( 9 | 10 | // LoginRequest 11 | // 登陆时所需要的参数 12 | LoginRequest struct { 13 | LoginName string `json:"login_name" binding:"required"` // 用户名 14 | Scrip string `json:"scrip" binding:"required"` // 编码 15 | } 16 | 17 | // LoginResponse 18 | // 登陆成功后的响应参数 19 | LoginResponse struct { 20 | SessionID string `json:"session_id"` 21 | AccessToken string `json:"access_token"` 22 | RefreshToken string `json:"refresh_token"` 23 | User string `json:"user"` 24 | } 25 | ) 26 | 27 | // Login 28 | // @BasePath /authorize/login 29 | // @Summary 登陆 30 | // @Description 登陆 31 | // @Accept json 32 | // @Produce json 33 | // @Param json body authorize.LoginRequest true "登陆时所需要的信息" 34 | // @Success 200 {object} internal.CommonResponse[authorize.LoginResponse] "token信息" 35 | // @Router /authorize/login [POST] 36 | func Login(ctx *internal.Context) { 37 | var loginRequest LoginRequest 38 | err := ctx.ShouldBind(&loginRequest) 39 | if err != nil { 40 | ctx.Fail(err) 41 | return 42 | } 43 | accessToken, refreshToken, err := service.AuthorizeService().Login(loginRequest.LoginName, loginRequest.Scrip) 44 | if err != nil { 45 | ctx.Fail(err) 46 | return 47 | } 48 | ctx.Success(&LoginResponse{ 49 | AccessToken: accessToken, 50 | RefreshToken: refreshToken, 51 | User: loginRequest.LoginName, 52 | }) 53 | } 54 | -------------------------------------------------------------------------------- /10-learn/internal/gateway/authorize/register.go: -------------------------------------------------------------------------------- 1 | package authorize 2 | 3 | import ( 4 | "onenet/internal" 5 | "onenet/internal/entity" 6 | "onenet/internal/service" 7 | "onenet/internal/util" 8 | ) 9 | 10 | type ( 11 | // RegisterRequest 12 | // 注册时所需要的参数 13 | RegisterRequest struct { 14 | Username string `json:"username" binding:"required"` // 用户名 15 | Password string `json:"password" binding:"required"` // 密码 16 | } 17 | ) 18 | 19 | // Register 20 | // @BasePath /authorize/register 21 | // @Summary 注册用户 22 | // @Description 注册用户 23 | // @Accept json 24 | // @Produce json 25 | // @Param json body authorize.RegisterRequest true "注册用户所需的信息" 26 | // @Success 200 {object} internal.CommonResponse[string] "请求成功" 27 | // @Router /authorize/register [POST] 28 | func Register(ctx *internal.Context) { 29 | var req RegisterRequest 30 | err := ctx.ShouldBind(&req) 31 | if err != nil { 32 | ctx.Fail(err) 33 | return 34 | } 35 | 36 | user := entity.User{} 37 | _ = util.Copy(&user, req) 38 | loginName, err := service.AuthorizeService().Register(&user, req.Password) 39 | if err != nil { 40 | ctx.Fail(err) 41 | return 42 | } 43 | ctx.Success(loginName) 44 | } 45 | -------------------------------------------------------------------------------- /10-learn/internal/gateway/authorize_valid.go: -------------------------------------------------------------------------------- 1 | package gateway 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "net/http" 6 | "onenet/internal" 7 | "onenet/internal/util/token" 8 | "strings" 9 | ) 10 | 11 | func AuthorizeValid() gin.HandlerFunc { 12 | return func(c *gin.Context) { 13 | ctx := &internal.Context{Context: c} 14 | bearerToken := ctx.GetHeader("Authorization") 15 | if len(strings.Split(bearerToken, " ")) != 2 { 16 | ctx.IndentedJSON(http.StatusUnauthorized, &internal.CommonResponse[interface{}]{ 17 | BizCode: http.StatusUnauthorized, 18 | Error: "未授权", 19 | }) 20 | ctx.Abort() 21 | return 22 | } 23 | tokenStr := strings.Split(bearerToken, " ")[1] 24 | payload, err := token.Inspect.VerifyToken(tokenStr) 25 | if err != nil { 26 | ctx.IndentedJSON(http.StatusUnauthorized, &internal.CommonResponse[interface{}]{ 27 | BizCode: http.StatusUnauthorized, 28 | Error: "未授权", 29 | }) 30 | ctx.Abort() 31 | return 32 | } 33 | err = token.ValidExpired(payload.ExpiredAt) 34 | if err != nil { 35 | ctx.IndentedJSON(http.StatusUnauthorized, &internal.CommonResponse[interface{}]{ 36 | BizCode: http.StatusUnauthorized, 37 | Error: "登陆超时", 38 | }) 39 | ctx.Abort() 40 | return 41 | } 42 | ctx.Set("authorize", payload) 43 | ctx.Next() 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /10-learn/internal/gateway/engine.go: -------------------------------------------------------------------------------- 1 | package gateway 2 | 3 | import ( 4 | "context" 5 | "github.com/gin-contrib/cors" 6 | "github.com/gin-gonic/gin" 7 | swaggerFiles "github.com/swaggo/files" 8 | ginSwagger "github.com/swaggo/gin-swagger" 9 | "net/http" 10 | "onenet/docs" 11 | "onenet/internal" 12 | "onenet/internal/gateway/album" 13 | "onenet/internal/gateway/authorize" 14 | "onenet/internal/util" 15 | "time" 16 | ) 17 | 18 | type RouterGroup struct { 19 | gin.RouterGroup 20 | } 21 | 22 | type Engine struct { 23 | Config util.Config 24 | Background *http.Server 25 | *gin.Engine 26 | } 27 | 28 | func (that *Engine) BindRoute() { 29 | if that.Config.Environment == "development" { 30 | that._bindByDev() 31 | } else if that.Config.Environment == "production" { 32 | that._bindByPro() 33 | } 34 | that._bind() 35 | } 36 | 37 | func (that *Engine) _bindByDev() { 38 | gin.SetMode(gin.DebugMode) 39 | // 允许跨域 40 | that.Use(cors.New(cors.Config{ 41 | AllowOrigins: that.Config.AllowedOrigins, 42 | AllowMethods: []string{ 43 | http.MethodPost, 44 | }, 45 | AllowHeaders: []string{ 46 | "Content-Type", 47 | "Authorization", 48 | }, 49 | AllowCredentials: true, 50 | })) 51 | 52 | // 加载swagger 53 | docs.SwaggerInfo.BasePath = "/" 54 | err := that.SetTrustedProxies([]string{"::1"}) 55 | if err != nil { 56 | return 57 | } 58 | that.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) 59 | } 60 | 61 | func (that *Engine) _bindByPro() { 62 | gin.SetMode(gin.ReleaseMode) 63 | } 64 | 65 | func (that RouterGroup) POST(relativePath string, handlers ...internal.HandlerFunc) RouterGroup { 66 | that.RouterGroup.POST(relativePath, internal.EnhanceContext(handlers[0])) 67 | return that 68 | } 69 | 70 | func (that RouterGroup) Group(relativePath string, handlers ...internal.HandlerFunc) RouterGroup { 71 | if len(handlers) != 0 { 72 | that.RouterGroup.Group(relativePath, internal.EnhanceContext(handlers[0])) 73 | } else { 74 | that.RouterGroup.Group(relativePath) 75 | } 76 | return that 77 | } 78 | 79 | func (that RouterGroup) Use(handlers ...gin.HandlerFunc) RouterGroup { 80 | that.RouterGroup.Use(handlers...) 81 | return that 82 | } 83 | 84 | func (that *Engine) _bind() { 85 | that.Use(gin.Logger()) 86 | that.Use(gin.Recovery()) 87 | 88 | routes := &RouterGroup{ 89 | that.RouterGroup, 90 | } 91 | routes.POST("/authorize/register", authorize.Register) 92 | routes.POST("/authorize/login", authorize.Login) 93 | 94 | authorizeRoute := routes.Group("/").Use(AuthorizeValid()) 95 | authorizeRoute.POST("/album/list", album.List) 96 | authorizeRoute.POST("/album/get_by_id", album.GetById) 97 | authorizeRoute.POST("/album/create_one", album.CreateOne) 98 | authorizeRoute.POST("/album/delete_by_id", album.DeleteById) 99 | } 100 | 101 | func Init(ctx context.Context, config util.Config) *Engine { 102 | engine := &Engine{ 103 | Engine: gin.New(), 104 | Config: config, 105 | } 106 | engine.BindRoute() 107 | return engine 108 | } 109 | 110 | func Run(engine *Engine) error { 111 | server := &http.Server{ 112 | Addr: engine.Config.HTTPServerAddress, 113 | Handler: engine, 114 | ReadTimeout: 10 * time.Second, 115 | WriteTimeout: 10 * time.Second, 116 | MaxHeaderBytes: 1 << 20, 117 | } 118 | engine.Background = server 119 | return engine.Background.ListenAndServe() 120 | } 121 | 122 | func Shutdown(ctx context.Context, engine *Engine) error { 123 | return engine.Background.Shutdown(ctx) 124 | } 125 | -------------------------------------------------------------------------------- /10-learn/internal/logic/album/album_service_impl.go: -------------------------------------------------------------------------------- 1 | package album 2 | 3 | import ( 4 | "fmt" 5 | "onenet/internal/dao" 6 | "onenet/internal/entity" 7 | "onenet/internal/service" 8 | ) 9 | 10 | const table = "album" 11 | 12 | type _albumServiceImpl struct { 13 | } 14 | 15 | func init() { 16 | impl := &_albumServiceImpl{} 17 | service.RegisterAlbumService(impl) 18 | } 19 | 20 | func (*_albumServiceImpl) ListByPage(content string, page int, size int) ([]*entity.Album, int, error) { 21 | if page >= 1 { 22 | page -= 1 23 | } 24 | queryJSON := fmt.Sprintf("{\"selector\":{\"$or\":[{\"title\":{\"$regex\":\""+content+"\"}},{\"summary\":{\"$regex\":\""+content+"\"}},{\"description\":{\"$regex\":\""+content+"\"}}]},\"sort\":[{\"created_at\":\"desc\"}],\"limit\":%d,\"skip\":%d}", size, page*size) 25 | results := dao.Couchdb.Find(table, queryJSON) 26 | if nil != results.Err() { 27 | return nil, 0, results.Err() 28 | } 29 | records := make([]*entity.Album, 0) 30 | for results.Next() { 31 | var album entity.Album 32 | err := results.ScanDoc(&album) 33 | if err != nil { 34 | return nil, 0, err 35 | } 36 | album.FirstPic = nil 37 | album.ID = album.Id 38 | records = append(records, &album) 39 | } 40 | return records, 0, nil 41 | } 42 | 43 | func (*_albumServiceImpl) CreateOne(album *entity.Album) (*entity.Album, error) { 44 | album.Number = 0 45 | album.OrderIndex = 0 46 | id, err := dao.Couchdb.Put(table, album) 47 | if err != nil { 48 | return nil, err 49 | } 50 | album.Id = id 51 | return album, nil 52 | } 53 | -------------------------------------------------------------------------------- /10-learn/internal/logic/authorize/authorize_service_impl.go: -------------------------------------------------------------------------------- 1 | package authorize 2 | 3 | import ( 4 | "fmt" 5 | "onenet/internal/consts" 6 | "onenet/internal/dao" 7 | "onenet/internal/entity" 8 | "onenet/internal/errors" 9 | "onenet/internal/service" 10 | "onenet/internal/util/token" 11 | ) 12 | 13 | const table = "user" 14 | 15 | type _authorizeServiceImpl struct{} 16 | 17 | func init() { 18 | impl := &_authorizeServiceImpl{} 19 | service.RegisterAuthorizeService(impl) 20 | } 21 | 22 | func (*_authorizeServiceImpl) Login(loginName, scrip string) (string, string, error) { 23 | ciphertext := _str2Ciphertext(scrip) 24 | query := fmt.Sprintf(`{"selector":{"$and":[{"username":"%s"},{"ciphertext":"%s"}]},"fields":["_id", "username"]}`, loginName, ciphertext) 25 | result := dao.Couchdb.Find(table, query) 26 | if !result.Next() { 27 | return "", "", &errors.BizError{ 28 | ErrorCode: 401, 29 | Msg: consts.LoginNameOrPassword, 30 | } 31 | } 32 | var user entity.User 33 | _ = result.ScanDoc(&user) 34 | accessToken, _, err := token.Inspect.CreateToken(user.Id, user.Username, []string{}, token.ACCESS) 35 | if err != nil { 36 | return "", "", err 37 | } 38 | refreshToken, _, err := token.Inspect.CreateToken(user.Id, user.Username, []string{}, token.REFRESH) 39 | if err != nil { 40 | return "", "", err 41 | } 42 | return accessToken, refreshToken, err 43 | } 44 | 45 | func (*_authorizeServiceImpl) Register(user *entity.User, pwd string) (string, error) { 46 | err := _readyRegister(user.Username) 47 | if err != nil { 48 | return "", err 49 | } 50 | user.Ciphertext = _str2Ciphertext(pwd) 51 | _, err = dao.Couchdb.Put(table, user) 52 | if err != nil { 53 | return "", err 54 | } 55 | return user.Username, err 56 | } 57 | -------------------------------------------------------------------------------- /10-learn/internal/logic/authorize/sub_imple.go: -------------------------------------------------------------------------------- 1 | package authorize 2 | 3 | import ( 4 | "fmt" 5 | gmssl "github.com/GmSSL/GmSSL-Go" 6 | "onenet/internal/consts" 7 | "onenet/internal/dao" 8 | "onenet/internal/errors" 9 | ) 10 | 11 | // _readyRegister 12 | // 判断用户是否已经注册 13 | func _readyRegister(username string) error { 14 | query := fmt.Sprintf(`{"selector":{"username":{"$eq":"%s"}},"fields":["_id"]}`, username) 15 | result := dao.Couchdb.Find(table, query) 16 | if result.Next() { 17 | return &errors.BizError{ 18 | ErrorCode: 400, 19 | Msg: fmt.Sprintf(consts.ReadyRegister, username), 20 | } 21 | } 22 | return nil 23 | } 24 | 25 | // _str2Ciphertext 26 | // 国密sm3 hash 27 | func _str2Ciphertext(str string) string { 28 | sm3 := gmssl.NewSm3() 29 | sm3.Update([]byte(str)) 30 | cipher := sm3.Digest() 31 | return fmt.Sprintf("%x", cipher) 32 | } 33 | -------------------------------------------------------------------------------- /10-learn/internal/logic/logic.go: -------------------------------------------------------------------------------- 1 | package logic 2 | 3 | import ( 4 | _ "onenet/internal/logic/album" 5 | _ "onenet/internal/logic/authorize" 6 | ) 7 | -------------------------------------------------------------------------------- /10-learn/internal/service/ialbum_service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "onenet/internal/entity" 5 | ) 6 | 7 | // IAlbumService 8 | // 相册相关接口 9 | type IAlbumService interface { 10 | 11 | // CreateOne 12 | // 创建相册 13 | // @Param *entity.Album 相册创建对象 14 | // @Return entity.Album 保存后的相册对象 15 | // @Return error bizError.BizError 16 | CreateOne(*entity.Album) (*entity.Album, error) 17 | 18 | // ListByPage 19 | // 查询相册 20 | // @Param string 相册名称/描述/简介 21 | // @Param int 第几页,从0开始 22 | // @Param int 每页多少条 23 | // @Return []*entity.Album 查询结果 24 | // @Return int 总数 25 | // @Return error bizError.BizError 26 | ListByPage(string, int, int) ([]*entity.Album, int, error) 27 | } 28 | 29 | var ( 30 | _albumService IAlbumService 31 | ) 32 | 33 | func RegisterAlbumService(i IAlbumService) { 34 | _albumService = i 35 | } 36 | 37 | func AlbumService() IAlbumService { 38 | if _albumService == nil { 39 | panic("albumService is nil") 40 | return nil 41 | } 42 | return _albumService 43 | } 44 | -------------------------------------------------------------------------------- /10-learn/internal/service/iauthorize_service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import "onenet/internal/entity" 4 | 5 | // IAuthorizeService 6 | // 授权相关接口 7 | type IAuthorizeService interface { 8 | 9 | // Register 10 | // 注册 11 | // @Param *entity.User 用户 12 | // @Param string 密码 13 | // @Return string 用户登陆凭证 14 | // @Return error 异常 15 | Register(*entity.User, string) (string, error) 16 | 17 | // Login 18 | // 登陆 19 | // @Param string 登陆名 20 | // @Param string 登陆凭证 21 | // @Return accessToken 访问token 22 | // @Return refreshToken 刷新token 23 | // @Return error 异常 24 | Login(string, string) (string, string, error) 25 | } 26 | 27 | var ( 28 | _authorizeService IAuthorizeService 29 | ) 30 | 31 | func RegisterAuthorizeService(service IAuthorizeService) { 32 | _authorizeService = service 33 | } 34 | 35 | func AuthorizeService() IAuthorizeService { 36 | if _authorizeService == nil { 37 | panic("authorizeService is nil") 38 | return nil 39 | } 40 | return _authorizeService 41 | } 42 | -------------------------------------------------------------------------------- /10-learn/internal/util/config.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/spf13/viper" 7 | ) 8 | 9 | // Config stores all configuration of the application. 10 | // The values are read by viper from a config file or environment variable. 11 | type Config struct { 12 | Environment string `mapstructure:"ENVIRONMENT"` 13 | AllowedOrigins []string `mapstructure:"ALLOWED_ORIGINS"` 14 | DBSource string `mapstructure:"DB_SOURCE"` 15 | DBUrl string `mapstructure:"DB_URL"` 16 | DBName string `mapstructure:"DB_NAME"` 17 | DBUser string `mapstructure:"DB_USER"` 18 | DBPassword string `mapstructure:"DB_PASSWORD"` 19 | MigrationURL string `mapstructure:"MIGRATION_URL"` 20 | RedisAddress string `mapstructure:"REDIS_ADDRESS"` 21 | HTTPServerAddress string `mapstructure:"HTTP_SERVER_ADDRESS"` 22 | GRPCServerAddress string `mapstructure:"GRPC_SERVER_ADDRESS"` 23 | TokenSymmetricKey string `mapstructure:"TOKEN_SYMMETRIC_KEY"` 24 | TokenType string `mapstructure:"TOKEN_TYPE"` 25 | AccessTokenDuration time.Duration `mapstructure:"ACCESS_TOKEN_DURATION"` 26 | RefreshTokenDuration time.Duration `mapstructure:"REFRESH_TOKEN_DURATION"` 27 | EmailSenderName string `mapstructure:"EMAIL_SENDER_NAME"` 28 | EmailSenderAddress string `mapstructure:"EMAIL_SENDER_ADDRESS"` 29 | EmailSenderPassword string `mapstructure:"EMAIL_SENDER_PASSWORD"` 30 | } 31 | 32 | // LoadConfig reads configuration from file or environment variables. 33 | func LoadConfig(path string) (config Config, err error) { 34 | viper.AddConfigPath(path) 35 | viper.SetConfigName("app") 36 | viper.SetConfigType("env") 37 | 38 | viper.AutomaticEnv() 39 | 40 | err = viper.ReadInConfig() 41 | if err != nil { 42 | return 43 | } 44 | 45 | err = viper.Unmarshal(&config) 46 | return 47 | } 48 | -------------------------------------------------------------------------------- /10-learn/internal/util/token/inspect.go: -------------------------------------------------------------------------------- 1 | package token 2 | 3 | var Inspect iTokenHelper // tokenHelper 实例 4 | 5 | // NewTokenMaker 构建TokenHelper实例 6 | func NewTokenMaker(symmetricKey, tokenType string) { 7 | if "paseto" == tokenType { 8 | Inspect = newPase2Inspect([]byte(symmetricKey)) 9 | } else { 10 | Inspect = newJWTInspect([]byte(symmetricKey)) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /10-learn/internal/util/token/jwt_impl.go: -------------------------------------------------------------------------------- 1 | package token 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "github.com/golang-jwt/jwt/v5" 7 | "time" 8 | ) 9 | 10 | type ( 11 | jwtImpl struct { 12 | symmetricKey []byte 13 | accessTokenDuration time.Duration 14 | refreshTokenDuration time.Duration 15 | } 16 | ) 17 | 18 | const ( 19 | timeLayout = "2006-01-02T15:04:05.999999-07:00" 20 | ) 21 | 22 | func newJWTInspect(symmetricKey []byte) iTokenHelper { 23 | return &jwtImpl{symmetricKey: symmetricKey} 24 | } 25 | 26 | func (that *jwtImpl) SetDurationTime(accessTokenDuration, refreshTokenDuration time.Duration) { 27 | that.accessTokenDuration = accessTokenDuration 28 | that.refreshTokenDuration = refreshTokenDuration 29 | } 30 | 31 | func (that *jwtImpl) CreateToken(id, username string, role []string, tokenType string) (string, *Payload, error) { 32 | var duration time.Duration 33 | if tokenType == ACCESS { 34 | duration = that.accessTokenDuration 35 | } else if tokenType == REFRESH { 36 | duration = that.refreshTokenDuration 37 | } 38 | payload := generatorPayload(id, username, role, duration) 39 | claims := jwt.MapClaims{} 40 | claims["id"] = payload.ID 41 | claims["username"] = payload.Username 42 | claims["role"] = payload.Role 43 | claims["issued_at"] = payload.IssuedAt 44 | claims["expired_at"] = payload.ExpiredAt 45 | token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) 46 | tokenStr, err := token.SignedString(that.symmetricKey) 47 | return tokenStr, payload, err 48 | } 49 | 50 | func (that *jwtImpl) VerifyToken(token string) (*Payload, error) { 51 | _token, err := jwt.Parse(token, func(token *jwt.Token) (any, error) { 52 | if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { 53 | return nil, errors.New(fmt.Sprintf("unexpected signing method: %v", token.Header["alg"])) 54 | } 55 | return that.symmetricKey, nil 56 | }) 57 | if err != nil { 58 | return nil, err 59 | } 60 | if !_token.Valid { 61 | return nil, errors.New("invalid token") 62 | } 63 | claims, ok := _token.Claims.(jwt.MapClaims) 64 | if !ok { 65 | return nil, errors.New("passer token failed") 66 | } 67 | 68 | payload := &Payload{ 69 | ID: claims["id"].(string), 70 | Username: claims["username"].(string), 71 | } 72 | 73 | iRoles := claims["role"].([]interface{}) 74 | if len(iRoles) == 0 { 75 | payload.Role = []string{} 76 | } else { 77 | roles := make([]string, len(iRoles)) 78 | for i, role := range iRoles { 79 | roles[i] = role.(string) 80 | } 81 | payload.Role = roles 82 | } 83 | issuedAtTime, _ := time.Parse(timeLayout, claims["issued_at"].(string)) 84 | payload.IssuedAt = issuedAtTime 85 | expiredAtTime, _ := time.Parse(timeLayout, claims["expired_at"].(string)) 86 | payload.ExpiredAt = expiredAtTime 87 | 88 | return payload, nil 89 | } 90 | -------------------------------------------------------------------------------- /10-learn/internal/util/token/paseto_impl.go: -------------------------------------------------------------------------------- 1 | package token 2 | 3 | import ( 4 | "github.com/o1egl/paseto" 5 | "time" 6 | ) 7 | 8 | type ( 9 | pasetoImpl struct { 10 | paseto *paseto.V2 11 | symmetricKey []byte 12 | accessTokenDuration time.Duration 13 | refreshTokenDuration time.Duration 14 | } 15 | ) 16 | 17 | func newPase2Inspect(symmetricKey []byte) iTokenHelper { 18 | return &pasetoImpl{ 19 | paseto: paseto.NewV2(), 20 | symmetricKey: symmetricKey, 21 | } 22 | } 23 | 24 | func (that *pasetoImpl) SetDurationTime(accessTokenDuration, refreshTokenDuration time.Duration) { 25 | that.accessTokenDuration = accessTokenDuration 26 | that.refreshTokenDuration = refreshTokenDuration 27 | } 28 | 29 | func (that *pasetoImpl) CreateToken(id, username string, role []string, tokenType string) (string, *Payload, error) { 30 | var duration time.Duration 31 | if tokenType == ACCESS { 32 | duration = that.accessTokenDuration 33 | } else if tokenType == REFRESH { 34 | duration = that.refreshTokenDuration 35 | } 36 | payload := generatorPayload(id, username, role, duration) 37 | token, err := that.paseto.Encrypt(that.symmetricKey, payload, nil) 38 | return token, payload, err 39 | } 40 | 41 | func (that *pasetoImpl) VerifyToken(token string) (*Payload, error) { 42 | payload := &Payload{} 43 | err := that.paseto.Decrypt(token, that.symmetricKey, payload, nil) 44 | return payload, err 45 | } 46 | -------------------------------------------------------------------------------- /10-learn/internal/util/token/payload.go: -------------------------------------------------------------------------------- 1 | package token 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | // Payload token中存储的信息 8 | type Payload struct { 9 | ID string `json:"id"` // 用户id 10 | Username string `json:"username"` // 用户名 11 | Role []string `json:"role"` // 用户角色列表 12 | IssuedAt time.Time `json:"issued_at"` // token创建时间 13 | ExpiredAt time.Time `json:"expired_at"` // token过期时间 14 | } 15 | -------------------------------------------------------------------------------- /10-learn/internal/util/token/token_helper.go: -------------------------------------------------------------------------------- 1 | package token 2 | 3 | import ( 4 | "errors" 5 | "time" 6 | ) 7 | 8 | const ( 9 | ACCESS = "access" 10 | REFRESH = "refresh" 11 | ) 12 | 13 | // ITokenHelper tokenHelper接口 14 | type iTokenHelper interface { 15 | // CreateToken 16 | // 为登陆用户创建token 17 | // @Param id string 用户id 18 | // @Param username string 用户名 19 | // @Param role []string 用户角色列表 20 | // @Param tokenType string token类型 access, refresh 21 | // @Return token, token.Payload, 异常 22 | CreateToken(id, username string, role []string, tokenType string) (string, *Payload, error) 23 | 24 | // VerifyToken 25 | // 验证Token字符串是否有效 26 | // @Param token string token字符串 27 | // @Return token.Payload,异常 28 | VerifyToken(token string) (*Payload, error) 29 | 30 | // SetDurationTime 31 | // 设置token存在时间 32 | // @Param accessTokenDuration 访问token存在时间 33 | // @Param refreshTokenDuration 刷新token存在时间 34 | SetDurationTime(accessTokenDuration, refreshTokenDuration time.Duration) 35 | } 36 | 37 | // ValidExpired 38 | // 验证token是否超时 39 | // Param expired 设置的时间 40 | // Return error 未超时返回nil 41 | func ValidExpired(expired time.Time) error { 42 | if time.Now().After(expired) { 43 | return errors.New("token 已过期") 44 | } 45 | return nil 46 | } 47 | 48 | // generatorPayload 49 | // 生成token封装信息 50 | // Param id string 用户id 51 | // Param username string 用户名 52 | // Param role []string 用户角色列表 53 | // Param duration time.Duration token存在时间 54 | // Return token.Payload 55 | func generatorPayload(id, username string, role []string, duration time.Duration) *Payload { 56 | return &Payload{ 57 | ID: id, 58 | Username: username, 59 | Role: role, 60 | IssuedAt: time.Now(), 61 | ExpiredAt: time.Now().Add(duration), 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /10-learn/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "golang.org/x/sync/errgroup" 6 | "log" 7 | "onenet/internal/dao" 8 | "onenet/internal/gateway" 9 | _ "onenet/internal/logic" 10 | "onenet/internal/util" 11 | "onenet/internal/util/token" 12 | "os" 13 | "os/signal" 14 | "syscall" 15 | ) 16 | 17 | var interruptSignals = []os.Signal{ 18 | os.Interrupt, 19 | syscall.SIGTERM, 20 | syscall.SIGINT, 21 | } 22 | 23 | func main() { 24 | log.Println("ready to start server") 25 | config, err := util.LoadConfig("./manifest") 26 | if nil != err { 27 | log.Printf("cannot load config") 28 | log.Panic(err) 29 | return 30 | } 31 | 32 | ctx, stop := signal.NotifyContext(context.Background(), interruptSignals...) 33 | defer stop() 34 | 35 | // TODO 数据库、redis、mq等中间件初始化 36 | runTokenKeyInit(config) 37 | runDBInit(config) 38 | 39 | waitGroup, ctx := errgroup.WithContext(ctx) 40 | // 启动对应的服务 41 | runGatewayServer(ctx, waitGroup, config) 42 | err = waitGroup.Wait() 43 | if err != nil { 44 | log.Printf("error from wait group") 45 | log.Panic(err) 46 | } 47 | } 48 | 49 | // 启动网关服务 50 | func runGatewayServer(ctx context.Context, group *errgroup.Group, config util.Config) { 51 | engine := gateway.Init(ctx, config) 52 | group.Go(func() error { 53 | log.Printf("start gateway server at %s", config.HTTPServerAddress) 54 | err := gateway.Run(engine) 55 | if err != nil { 56 | log.Println("failed to start gateway server") 57 | } 58 | return nil 59 | }) 60 | group.Go(func() error { 61 | <-ctx.Done() 62 | log.Println("graceful shutdown gateway server") 63 | err := gateway.Shutdown(ctx, engine) 64 | if err != nil { 65 | log.Println("failed to shutdown gateway server") 66 | return err 67 | } 68 | log.Println("gateway server is stopped") 69 | return nil 70 | }) 71 | } 72 | 73 | func runDBInit(config util.Config) { 74 | dao.Register(config) 75 | log.Println("register db success") 76 | } 77 | 78 | func runTokenKeyInit(config util.Config) { 79 | token.NewTokenMaker(config.TokenSymmetricKey, config.TokenType) 80 | token.Inspect.SetDurationTime(config.AccessTokenDuration, config.RefreshTokenDuration) 81 | } 82 | -------------------------------------------------------------------------------- /10-learn/manifest/app.env: -------------------------------------------------------------------------------- 1 | ENVIRONMENT=development 2 | ALLOWED_ORIGINS=http://localhost:3000,https://simplebank.com 3 | DB_SOURCE=postgresql://root:secret@localhost:5432/simple_bank?sslmode=disable 4 | DB_URL=localhost:5984 5 | DB_NAME=onenet 6 | DB_USER=admin 7 | DB_PASSWORD=111111hZ! 8 | MIGRATION_URL=file://db/migration 9 | HTTP_SERVER_ADDRESS=0.0.0.0:8080 10 | GRPC_SERVER_ADDRESS=0.0.0.0:9090 11 | TOKEN_SYMMETRIC_KEY=12345678901234567890123456789012 12 | TOKEN_TYPE=paseto 13 | ACCESS_TOKEN_DURATION=1h 14 | REFRESH_TOKEN_DURATION=24h 15 | REDIS_ADDRESS=0.0.0.0:6379 16 | EMAIL_SENDER_NAME=Simple Bank 17 | EMAIL_SENDER_ADDRESS=simplebanktest@gmail.com 18 | EMAIL_SENDER_PASSWORD=jekfcygyenvzekke 19 | -------------------------------------------------------------------------------- /10-learn/use_gmssl.md: -------------------------------------------------------------------------------- 1 | 在go中使用国密算法 2 | === 3 | 4 | 在国产化中数据库推荐使用达梦、人大金仓、神通等,加密算法自然是国密,Go在使用国密算法的时候需要使用如下库 5 | https://github.com/GmSSL/GmSSL-Go 6 | 7 | 而GmSSL是由北京大学自主开发的国产商用密码开源库,实现了对国密算法、标准和安全通信协议的全面功能覆盖,支持包括移动端在内的主流操作系统和处理器, 8 | 支持密码钥匙、密码卡等典型国产密码硬件,提供功能丰富的命令行工具及多种编译语言编程接口。 9 | https://github.com/guanzhi/GmSSL 10 | 11 | ## 安装前准备 12 | 下载源码并进行编译和安装 13 | ```shell 14 | cd ~/.local/src 15 | git clone git@github.com:guanzhi/GmSSL.git 16 | 17 | cd GmSSL 18 | mkdir build 19 | cd build 20 | cmake .. 21 | make 22 | make test 23 | sudo make install 24 | 25 | cd ~/.local/lib 26 | ls | grep gmssl 27 | libgmssl.so libgmssl.so.3 libgmssl.so.3.1 28 | 29 | cd ~/.local/include 30 | ls | grep gmssl 31 | gmssl 32 | ``` 33 | 显示如上内容表示密码库安装完毕 34 | 35 | ## Go调用 36 | ```shell 37 | go get github.com/GmSSL/GmSSL-Go 38 | ``` 39 | 若未安装国密库,该模块是无法进行安装的 40 | ```go 41 | // _str2Ciphertext 42 | // 国密sm3 hash 43 | func _str2Ciphertext(str string) string { 44 | sm3 := gmssl.NewSm3() 45 | sm3.Update([]byte(str)) 46 | cipher := sm3.Digest() 47 | return fmt.Sprintf("%x", cipher) 48 | } 49 | ``` 50 | 使用的是国密sm3 hash算法,可参考[internal/logic/authorize/sub_imple.go](internal/logic/authorize/sub_imple.go)的27行 51 | 52 | 更多算法可参照GmSSL的 github。 53 | -------------------------------------------------------------------------------- /10-learn/use_swagger.md: -------------------------------------------------------------------------------- 1 | 在go中使用Swagger 2 | === 3 | 4 | Swagger 的优点这里就不赘述了。简单说下怎么在go中使用swagger。 5 | 6 | Swagger主要的库为 7 | 静态文件 https://github.com/swaggo/files 8 | gin插件和处理器 https://github.com/swaggo/gin-swagger 9 | swagger主体 https://github.com/swaggo/swag 10 | 11 | ## 环境准备 12 | 13 | ### 安装命令行插件 14 | 该插件 swag 将Go的注释转换为Swagger2.0文档。 15 | 16 | ```shell 17 | go install github.com/swaggo/swag/cmd/swag@latest 18 | swag --version 19 | swag version v1.16.4 20 | ``` 21 | 22 | ## 使用 23 | ### 编写注释 24 | ```go 25 | package authorize 26 | 27 | import ( 28 | "onenet/internal" 29 | "onenet/internal/service" 30 | ) 31 | 32 | type ( 33 | 34 | // LoginRequest 35 | // 登陆时所需要的参数 36 | LoginRequest struct { 37 | LoginName string `json:"login_name" binding:"required"` // 用户名 38 | Scrip string `json:"scrip" binding:"required"` // 编码 39 | } 40 | 41 | // LoginResponse 42 | // 登陆成功后的响应参数 43 | LoginResponse struct { 44 | SessionID string `json:"session_id"` 45 | AccessToken string `json:"access_token"` 46 | RefreshToken string `json:"refresh_token"` 47 | User string `json:"user"` 48 | } 49 | ) 50 | 51 | // Login 52 | // @BasePath /authorize/login 53 | // @Summary 登陆 54 | // @Description 登陆 55 | // @Accept json 56 | // @Produce json 57 | // @Param json body authorize.LoginRequest true "登陆时所需要的信息" 58 | // @Success 200 {object} internal.CommonResponse[authorize.LoginResponse] "token信息" 59 | // @Router /authorize/login [POST] 60 | func Login(ctx *internal.Context) { 61 | var loginRequest LoginRequest 62 | err := ctx.ShouldBind(&loginRequest) 63 | if err != nil { 64 | ctx.Fail(err) 65 | return 66 | } 67 | accessToken, refreshToken, err := service.AuthorizeService().Login(loginRequest.LoginName, loginRequest.Scrip) 68 | if err != nil { 69 | ctx.Fail(err) 70 | return 71 | } 72 | ctx.Success(&LoginResponse{ 73 | AccessToken: accessToken, 74 | RefreshToken: refreshToken, 75 | User: loginRequest.LoginName, 76 | }) 77 | } 78 | ``` 79 | 详细可参考[internal/gateway](internal/gateway)下的包内 80 | 81 | ### 生成swagger json 82 | ```shell 83 | swag init 84 | 85 | 2024/11/05 15:12:02 Generate swagger docs.... 86 | 2024/11/05 15:12:02 Generate general API Info, search dir:./ 87 | 2024/11/05 15:12:02 Generating album.CreateAlbumRequest 88 | 2024/11/05 15:12:02 Generating internal.CommonResponse-album_CreateAlbumResponse 89 | 2024/11/05 15:12:02 Generating album.CreateAlbumResponse 90 | 2024/11/05 15:12:02 Generating album.ListRequest 91 | 2024/11/05 15:12:02 Generating internal.PageParam 92 | 2024/11/05 15:12:02 Generating internal.CommonResponse-album_ListResponse 93 | 2024/11/05 15:12:02 Generating album.ListResponse 94 | 2024/11/05 15:12:02 Generating entity.Album 95 | 2024/11/05 15:12:02 Generating entity.Picture 96 | 2024/11/05 15:12:02 Generating entity._base 97 | 2024/11/05 15:12:02 Generating authorize.LoginRequest 98 | 2024/11/05 15:12:02 Generating internal.CommonResponse-authorize_LoginResponse 99 | 2024/11/05 15:12:02 Generating authorize.LoginResponse 100 | 2024/11/05 15:12:02 Generating authorize.RegisterRequest 101 | 2024/11/05 15:12:02 Generating internal.CommonResponse-string 102 | 2024/11/05 15:12:02 create docs.go at docs/docs.go 103 | 2024/11/05 15:12:02 create swagger.json at docs/swagger.json 104 | 2024/11/05 15:12:02 create swagger.yaml at docs/swagger.yaml 105 | ``` 106 | 生成的swagger 文件在[docs](docs) 下 107 | 108 | ### 配置Swagger 109 | ```go 110 | package gateway 111 | 112 | import ( 113 | swaggerFiles "github.com/swaggo/files" 114 | ginSwagger "github.com/swaggo/gin-swagger" 115 | "onenet/docs" 116 | ) 117 | 118 | func (that *Engine) _bindByDev() { 119 | gin.SetMode(gin.DebugMode) 120 | // 允许跨域 121 | that.Use(cors.New(cors.Config{ 122 | AllowOrigins: that.Config.AllowedOrigins, 123 | AllowMethods: []string{ 124 | http.MethodPost, 125 | }, 126 | AllowHeaders: []string{ 127 | "Content-Type", 128 | "Authorization", 129 | }, 130 | AllowCredentials: true, 131 | })) 132 | 133 | // 加载swagger 134 | docs.SwaggerInfo.BasePath = "/" 135 | err := that.SetTrustedProxies([]string{"::1"}) 136 | if err != nil { 137 | return 138 | } 139 | that.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) 140 | } 141 | ``` 142 | 注意,要在handel里把 doc拉进来,否则swagger json会失败。 该代码位于[internal/gateway/engine.go](internal/gateway/engine.go)的`37`行 143 | 144 | 145 | ## 启动服务并验证 146 | ```shell 147 | go run . 148 | 149 | [GIN-debug] GET /swagger/*any --> github.com/swaggo/gin-swagger.CustomWrapHandler.func1 (2 handlers) 150 | [GIN-debug] POST /authorize/register --> onenet/internal/gateway.RouterGroup.POST.EnhanceContext.func1 (4 handlers) 151 | [GIN-debug] POST /authorize/login --> onenet/internal/gateway.RouterGroup.POST.EnhanceContext.func1 (4 handlers) 152 | [GIN-debug] POST /album/list --> onenet/internal/gateway.RouterGroup.POST.EnhanceContext.func1 (5 handlers) 153 | [GIN-debug] POST /album/get_by_id --> onenet/internal/gateway.RouterGroup.POST.EnhanceContext.func1 (5 handlers) 154 | [GIN-debug] POST /album/create_one --> onenet/internal/gateway.RouterGroup.POST.EnhanceContext.func1 (5 handlers) 155 | [GIN-debug] POST /album/delete_by_id --> onenet/internal/gateway.RouterGroup.POST.EnhanceContext.func1 (5 handlers) 156 | ``` 157 | 访问地址: http://localhost:8080/swagger/index.html 158 | ![swaggerImage](wechat_2024-11-05_151938_084.png) -------------------------------------------------------------------------------- /10-learn/wechat_2024-11-05_151938_084.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afterloe/golangStudy/5e895761ed4738304ddfaf930221df18ca580581/10-learn/wechat_2024-11-05_151938_084.png -------------------------------------------------------------------------------- /2-learn/1-test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | /** 6 | 函数 定义如下 7 | 8 | func funcName(input1 type, input2 type) (output1 type, output2 type) { 9 | // ... do something 10 | return value1, value2 11 | } 12 | 13 | go里面可以为函数的返回值申明变量,当然这种写法也是可以的 14 | 15 | func funcName(input1 type, input2 type) (type, type) { 16 | 如果没有返回值 可以忽略 return、有的话 必须写上 return 17 | */ 18 | func sum(a, b int) int { 19 | return a + b 20 | } 21 | 22 | /** 23 | 多参数返回, 简写 24 | */ 25 | func sumAndMax(a, b int) (int, int) { 26 | max := 0 27 | if a > b { 28 | max = a 29 | } else { 30 | max = b 31 | } 32 | 33 | return sum(a, b), max 34 | } 35 | /** 36 | 官方建议 37 | func sumAndMax2(a, b int)(sum int, max int) { 38 | // do something 39 | return 40 | } 41 | */ 42 | 43 | /* 可变参数 44 | func myFunc(arg ... int) { 45 | for _, n : range arg { 46 | fmt.Printf("And the number is: %d \n", n) 47 | } 48 | } 49 | */ 50 | /* 值传递和指针传递 别忘了 51 | func add1(a *int) int { 52 | // 这是会修改指针指向的值,基本就是全局修改了。而值传递是不会进行修改的。只是copy一份数据 53 | } 54 | 55 | 传指针使得多个函数能操作同一个对象。 56 | 传指针比较轻量级(8bytes),只是传内存地址 57 | string,slice,map这三种类型的实现机制类似指针,所以可以直接传递,而不用取地址后传递指针 58 | */ 59 | 60 | /* 61 | defer 延迟(defer)语句,你可以在函数中添加多个defer语句。当函数执行到最后时,这些defer语句会 按照逆序执行,最后该函数返回 62 | 进行一些打开资源的操作时,遇到错误需要提前返回,在返回前你需要关闭相应的 资源,不然很容易造成资源泄露等问题 63 | 64 | func readFile() bool { 65 | file.Open("file") 66 | if errX { 67 | file.close() 68 | return false; 69 | } 70 | if errY { 71 | file.close() 72 | return false; 73 | } 74 | file.close() 75 | return true; 76 | } 77 | // 优化后 78 | func readFile() bool { 79 | file.Open("file") 80 | defer file.Close() // defer 会在函数退出前调用 81 | if errX { 82 | return false; 83 | } 84 | if errY { 85 | return false; 86 | } 87 | return true; 88 | } 89 | // 如果有很多调用defer,那么defer是采用后进先出模式 90 | 91 | 打开文件 92 | 做一些事情 93 | 关闭文件 94 | 记录日志 95 | */ 96 | 97 | func mockReadFile() { 98 | fmt.Println("打开文件") 99 | defer fmt.Println("记录日志") 100 | defer fmt.Println("关闭文件") 101 | fmt.Println("做一些事情") 102 | } 103 | 104 | // 同脚本语言一样,golang 也是可以将函数 作为值、类型进行传递的 通过type来定义它,它的类型就是所有拥有相同的参数,相同的返回值 105 | /* 106 | type typeName func(input1 type, input2 type) (return1 type, return2 type) 107 | */ 108 | type testInt func (int, int) int // 定义一个函数类型 109 | 110 | // 函数当做值和类型 写一些通用接口的时候非常有用 111 | func useType(a int, b int, f testInt) (value int) { 112 | value = f(a, b) 113 | return 114 | } 115 | 116 | // go 异常处理 117 | /* 118 | 119 | */ 120 | 121 | func main() { 122 | var ( 123 | a = 13 124 | b = 11 125 | ) 126 | 127 | fmt.Println("a > b ? => ", a > b) 128 | 129 | // 计算获取c的值 然后判断c的值是不是大于10 130 | if c := a + b; c > 10 { 131 | fmt.Println("a > b", c) 132 | // goto 语法 标签名是大小写敏感的 133 | goto GOThere 134 | } else { 135 | fmt.Println("a < b", c) 136 | } 137 | 138 | GOThere: // goto 标签 139 | fmt.Printf("sum is %d \n", sum(a, b)) 140 | fmt.Println("i go here") 141 | //fmt.Println(c) 外部是不能使用这个c的 他的域不在这. 但是这里是可以的,因为这个c不是上面那个 142 | c , d := sumAndMax(a, b) 143 | fmt.Printf("sum is %d, max is %d \n", c, d) 144 | 145 | // 简单的循环逻辑 146 | sumValue := 0 147 | for sumValue < 100 { 148 | sumValue+=1 149 | } 150 | fmt.Printf("sum is %d \n", c) 151 | 152 | // for 也可以配合 range 用于读取 splice 和 map的 数据 153 | simpleMap := map[string]int { "one":1, "two":2, "three":3 } 154 | for key, value:= range simpleMap { 155 | fmt.Printf("simpleMap key is %s \n", key) 156 | fmt.Printf("simapleMap Value is %d \n", value) 157 | } 158 | 159 | // switch 用法大多地方是一致的 160 | switch a { 161 | case 10: 162 | fmt.Println("this is 10") 163 | default: 164 | fmt.Println("no value is show.") 165 | } 166 | 167 | mockReadFile() 168 | fmt.Printf("value is %d", useType(1001, 20031, sum)) 169 | } 170 | -------------------------------------------------------------------------------- /2-learn/2-test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | core "fmt" // 这种导入是可以指定 导入包的名字的,如果不指定。默认是用 包名 5 | //_ "fmt" // 这种不是导入,而是调用了 fmt 里面的 init 函数 6 | ) 7 | 8 | /* 9 | 定义 struct 10 | */ 11 | type person struct { 12 | name string 13 | mail string 14 | age int8 15 | } 16 | 17 | func personToString (p *person) { 18 | core.Println(p.name) 19 | core.Println(p.mail) 20 | core.Println(p.age) 21 | } 22 | 23 | func olderMan(p1, p2 *person) (older *person) { 24 | if p1.age > p2.age { 25 | older = p1 26 | } else { 27 | older = p2 28 | } 29 | return 30 | } 31 | 32 | /* 33 | go 是支持只提供类型,二不写字段的方式来申明struct 34 | */ 35 | type developer struct { 36 | person // 可以理解为继承吧。。。 包含了person的所有字段 37 | domain string 38 | //age int8 39 | } 40 | 41 | func main() { 42 | core.Println("指定别名 导入外部包") 43 | // struct 使用 - 第一种使用方式 44 | var me person 45 | me.name = "afterloe" 46 | me.mail = "lm6289511@gmail.com" 47 | me.age = 24 48 | 49 | // 第二种使用方式,这种必须要补全 - 否则会报错 50 | he := person{"afterloe.L","lm6289511@gmail.com", 21} 51 | personToString(&he) // 打印出person的内容 52 | 53 | // 第三种,可以缺省 54 | he2 := person{mail: "lm6289511@gmail.com", age: 17} 55 | older := olderMan(&he, &he2) // 注意这里返回的是 person 的指针 56 | personToString(older) 57 | 58 | 59 | older.age = 4 // 指针修改的是 执行的内容,如果发生修改 所有的东西都进行了修改的 60 | personToString(&he) 61 | 62 | goLangDeveloper := developer{he2, "golang"} // 申明一个程序员 63 | personToString(&goLangDeveloper.person) 64 | core.Println("worker age is ", goLangDeveloper.age) // 字段是可以直接访问的! 65 | // 如果覆盖的话 ,以最外层的为准。 当然如果想访问最原来的值可以用下面的方法 66 | core.Println("golang developer age is ", goLangDeveloper.person.age) 67 | core.Println("developer domain is ", goLangDeveloper.domain) 68 | } 69 | -------------------------------------------------------------------------------- /2-learn/3-test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type Animal struct { 8 | name string 9 | age int 10 | } 11 | 12 | type Dog struct { 13 | Animal 14 | } 15 | 16 | type Cat struct { 17 | Animal 18 | } 19 | 20 | // 使用 占位符 但是不能调用原来的内容 21 | func (_ Animal) say() { 22 | fmt.Println("吼~~") 23 | } 24 | 25 | // 给struct 绑定方法 26 | func (d Dog) say() { 27 | // 调用d的参数 28 | fmt.Println(d.name, "say 汪汪汪") 29 | } 30 | 31 | // 可以使值传递 也可以是 指针传递 32 | // 指针会对实例对象的内容发生操作,而普通类型是以副本作为操作对象,并不对原实例对象发生操作 33 | func (c *Cat) say() { 34 | fmt.Println(c.name,"say 喵喵喵") 35 | } 36 | 37 | // method是可以再任何自定义的类型、内置类型、struct等各种类型上的 38 | // 其他自定义类型申明如下 39 | // type typeName typeLiteral 40 | type months map[string]int 41 | 42 | func main() { 43 | littleDog := Dog{Animal{"小黑", 2}} 44 | littleDog.say() 45 | littleCat := Cat{Animal{"小花", 1}} 46 | littleCat.say() 47 | littleDog = Dog{} 48 | littleDog.Animal.say() 49 | 50 | // 使用自定义类型 51 | m := months{"January": 31, "February": 28} 52 | for key, value := range m { 53 | fmt.Println(key, " --> ", value) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /2-learn/4-test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strconv" // Itoa 方法可以把 int 转换为 string 6 | ) 7 | 8 | type Person struct { 9 | name string 10 | age int 11 | } 12 | 13 | type Developer struct { 14 | Person 15 | domain string 16 | tags map[string] int 17 | } 18 | 19 | // 申明一个 interface 包含两个方法 20 | type HumanActivity interface { 21 | say(word, time string) 22 | doSomething() 23 | } 24 | 25 | // 申明一个空的interface 他是可以存储接收任意数值,可以理解为 void * 26 | type voidActivity interface { 27 | 28 | } 29 | 30 | func (p Person) say (word, time string) { 31 | fmt.Printf("%s\t[%s]: %s \n", p.name, time, word) 32 | } 33 | 34 | // 自己实现 35 | func (_ Person) doSomething() { 36 | fmt.Println("want to do something ...") 37 | } 38 | 39 | func (developer Developer) say(word, time string) { 40 | fmt.Printf("程序员[%s][%s]: %s \n", developer.domain, time, word) 41 | } 42 | 43 | /** 44 | 重写 toString 方法 45 | */ 46 | func (p Person) String() string { 47 | return "name is " + p.name + " age is " + strconv.Itoa(p.age) 48 | } 49 | 50 | func (developer Developer) doSomething() { 51 | fmt.Printf("%s -------------------------- \n", developer.domain) 52 | for key, val := range developer.tags { 53 | fmt.Printf("%s -> %d \n", key, val) 54 | } 55 | } 56 | 57 | func main() { 58 | joe := Person{name: "joe", age: 6} 59 | yyy := Person{name: "yyy", age: 5} 60 | grace := Developer{Person{name: "grace", age: 25}, "java", map[string]int {"j2ee":2, "j2me":5}} 61 | tom := Developer{Person{name: "grace", age: 25}, "go", map[string]int {"info": 0}} 62 | time := "2018-01-30 14:12:15" 63 | 64 | // 申明一个interface 变量 65 | var activity HumanActivity 66 | activity = joe // 如果这个struct 有这个方法(参数列表也要一致 就能赋值) 67 | activity.say("今天", time) 68 | activity.doSomething() // 直接调用方法 69 | 70 | activity = grace 71 | activity.say("明天", time) 72 | activity.doSomething() 73 | 74 | actList := make([]HumanActivity, 3) 75 | actList[0], actList[1], actList[2] = yyy, tom, grace // 也能用 slice 进行结构 76 | 77 | // 支持循环调用 78 | for _, value := range actList { 79 | value.doSomething() 80 | } 81 | 82 | // 空 interface 的运用 83 | i := 10 84 | var va voidActivity 85 | va = i 86 | s1 := "hello world" 87 | va = s1 88 | fmt.Printf("%s \n", va) 89 | fmt.Println("joe is ", joe) // 调用默认的toString 方法 90 | fmt.Println("tom is ", tom.String()) // 调用父级的toString 方法 91 | 92 | // comma-ok 断言 93 | // 是否确实 存储了T类型的数值 94 | // value就是变量的值,ok是一个bool类 型,element是interface变量,T是断言的类型 95 | if value, ok := activity.(Developer); ok { 96 | fmt.Println("activity 是Developer 值是", value) 97 | } 98 | 99 | // 如果element里面确实存储了T类型的数值,那么ok返回true,否则返回false。 100 | for _, element := range actList { 101 | // 判断 数组中的 interface 是不是特定的类型 102 | if value, ok := element.(Developer); ok { 103 | fmt.Println("has info", value, ok) 104 | } 105 | } 106 | /* 107 | // 还可以用switch case 来处理 不过 element.(type)语法不能在switch外使用 108 | switch value := element.(type) { 109 | case Developer: 110 | break; 111 | case Person; 112 | break; 113 | default: 114 | break; 115 | } 116 | */ 117 | } 118 | -------------------------------------------------------------------------------- /2-learn/struct.md: -------------------------------------------------------------------------------- 1 | # 结构体 struct 2 | 3 | > 代码链接 [./2-test.go](./2-test.go) 4 | 5 | struct不仅能将struct作为匿名字段,自定义类型、内置类型都可以作为匿名字段,而且可以在相应的字段上进行函数操作 6 | 7 | ```golang 8 | /* 9 | go 是支持只提供类型,二不写字段的方式来申明struct 10 | */ 11 | type developer struct { 12 | person // 可以理解为继承吧。。。 包含了person的所有字段 13 | domain string 14 | //age int8 15 | } 16 | 17 | goLangDeveloper := developer{he2, "golang"} // 申明一个程序员 18 | personToString(&goLangDeveloper.person) 19 | core.Println("worker age is ", goLangDeveloper.age) // 字段是可以直接访问的! 20 | // 如果覆盖的话 ,以最外层的为准。 当然如果想访问最原来的值可以用下面的方法 21 | core.Println("golang developer age is ", goLangDeveloper.person.age) 22 | core.Println("developer domain is ", goLangDeveloper.domain) 23 | ``` 24 | 25 | 具体可以参考源代码的内容 26 | 27 | 指针返回内容如`func olderMan(p1, p2 *person) (older *person)`, 详见[2-test.go](./2-test.go) 28 | 29 | 30 | -------------------------------------------------------------------------------- /2-learn/summary.md: -------------------------------------------------------------------------------- 1 | # 第二天 2 | 3 | > 代码链接 [./1-test.go](./1-test.go) 4 | 5 | 函数 定义如下 6 | 7 | ``` 8 | func funcName(input1 type, input2 type) (output1 type, output2 type) { 9 | // ... do something 10 | return value1, value2 11 | } 12 | ``` 13 | go里面可以为函数的返回值申明变量,当然这种写法也是可以的 14 | `func funcName(input1 type, input2 type) (type, type) {}` 15 | 16 | * ps: golang 没有三元表达式~~ 17 | 18 | defer 延迟(defer)语句,你可以在函数中添加多个defer语句。当函数执行到最后时,这些defer语句会 按照逆序执行,最后该函数返回进行一些打开资源的操作时,遇到错误需要提前返回,在返回前你需要关闭相应的 资源,不然很容易造成资源泄露等问题 19 | 20 | ```golang 21 | func readFile() bool { 22 | file.Open("file") 23 | if errX { 24 | file.close() 25 | return false; 26 | } 27 | if errY { 28 | file.close() 29 | return false; 30 | } 31 | file.close() 32 | return true; 33 | } 34 | ``` 35 | 使用`defer`优化之后 36 | ```golang 37 | func readFile() bool { 38 | file.Open("file") 39 | defer file.Close() // defer 会在函数退出前调用 40 | if errX { 41 | return false; 42 | } 43 | if errY { 44 | return false; 45 | } 46 | return true; 47 | } 48 | ``` 49 | 如果有很多调用defer,那么defer是采用后进先出的方式执行 50 | ```golang 51 | func mockReadFile() { 52 | fmt.Println("打开文件") 53 | defer fmt.Println("记录日志") 54 | defer fmt.Println("关闭文件") 55 | fmt.Println("做一些事情") 56 | } 57 | 58 | /* 59 | 打开文件 60 | 做一些事情 61 | 关闭文件 62 | 记录日志 63 | */ 64 | ``` 65 | 66 | 同脚本语言一样,golang 也是可以将函数 作为值、类型进行传递的 通过type来定义它,它的类型就是所有拥有相同的参数,相同的返回值 67 | ```golang 68 | type testInt func (int, int) int // 定义一个函数类型 69 | 70 | // 函数当做值和类型 写一些通用接口的时候非常有用 71 | func useType(a int, b int, f testInt) (value int) { 72 | value = f(a, b) 73 | return 74 | } 75 | ``` 76 | 77 | `for` 也可以配合 range 用于读取 splice 和 map的 数据 78 | ```golang 79 | simpleMap := map[string]int { "one":1, "two":2, "three":3 } 80 | for key, value:= range simpleMap { 81 | fmt.Printf("simpleMap key is %s \n", key) 82 | fmt.Printf("simapleMap Value is %d \n", value) 83 | } 84 | ``` 85 | go是支持使用指针传参的,所以传入指针的时候时候,修改是会对所有的东西进行修改。而且和java的继承方式一样,也有继承、重写这两种操作,差距不大。 86 | 87 | interface能够接受struct的函数,但是函数描述必须一致,参数列表和返回内容也是一样的才能进行代理。 88 | struct的`String`方法类似于java的`toString`的写法。而空interface是可以接受任何参数 89 | 90 | ```golan 91 | type voidActivity interface {} 92 | ``` 93 | 94 | 而且interface 是支持数组列表循环调用的,而且 `comma-ok`断言可以 来确认是否存储了T类型。value就是变量的值,ok是一个bool类 型,element是interface变量,T是断言的类型 95 | ```golan 96 | value, ok = element.(T) 97 | ``` 98 | 如果element里面确实存储了T类型的数值,那么ok返回true,否则返回false。 99 | ```golan 100 | if value, ok := activity.(Developer); ok { 101 | fmt.Println("activity 是Developer 值是", value) 102 | } 103 | ``` 104 | 105 | 当然也是可以用以下方法 106 | ```golan 107 | actList := make([]HumanActivity, 3) 108 | actList[0], actList[1], actList[2] = yyy, tom, grace // 也能用 slice 进行结构 109 | 110 | for _, element := range actList { 111 | // 判断 数组中的 interface 是不是特定的类型 112 | if value, ok := element.(Developer); ok { 113 | fmt.Println("has info", value, ok) 114 | } 115 | } 116 | 117 | switch value := element.(type) { 118 | case Developer: 119 | break; 120 | case Person; 121 | break; 122 | default: 123 | break; 124 | } 125 | ``` 126 | -------------------------------------------------------------------------------- /3-learn/1-test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type Test struct { 8 | name string 9 | } 10 | 11 | type vo1 interface { 12 | equal() 13 | } 14 | 15 | // 嵌入式 interface 理解为 interface 的继承 16 | type vo2 interface { 17 | vo1 18 | say(vo1) string // 也可以使用 函数传参的形式 19 | } 20 | 21 | func (t Test) equal() { 22 | fmt.Println("equal " + t.name) 23 | } 24 | 25 | func (t Test) say(v vo1) string{ 26 | fmt.Println("---------------------------") 27 | v.equal() 28 | fmt.Println("say ...") 29 | return t.name 30 | } 31 | 32 | func main() { 33 | t := Test{name: "afterloe"} 34 | var v1 vo1 35 | v1 = t 36 | v1.equal() 37 | 38 | t1 := Test{name: "Joy"} 39 | var v2 vo2 40 | v2 = t1 41 | v2.say(v1) // 嵌入式函数调用 42 | v2.equal() 43 | } -------------------------------------------------------------------------------- /3-learn/2-test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | type voidI interface {} 9 | 10 | func getType(i voidI) { 11 | t := reflect.TypeOf(i) // 获取空函数的类型 12 | fmt.Println(t) // 输出 13 | v := reflect.ValueOf(i) // 获取值 14 | fmt.Println(v) 15 | } 16 | 17 | /* 18 | interface 的字段是不可以修改的 19 | 20 | func change(i *voidI) { 21 | p := reflect.ValueOf(i) // 获取值的地址 22 | v := p.Elem() // 转换为 Elem对象 23 | v.SetFloat(7.5) // 24 | } 25 | */ 26 | 27 | func main() { 28 | var x = 4.5 29 | getType(x) // float64 30 | var d = "afterloe" 31 | getType(d) // string 32 | 33 | // 如果类型不一致是会报错的 34 | //change(x) 35 | //fmt.Println(x) 36 | 37 | //var i voidI 38 | //i = 3.5 39 | //change(&i) 40 | fmt.Println(x) 41 | p := reflect.ValueOf(&x) 42 | v := p.Elem() 43 | v.SetFloat(3.14) 44 | //v.SetString("afterloe") // 类型必须是一致的才可以修改 45 | getType(x) 46 | } 47 | -------------------------------------------------------------------------------- /3-learn/3-test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | ) 7 | 8 | func say(word string) { 9 | for i:=0; i < 10; i++ { 10 | runtime.Gosched() // 让CPU把时间片让给别人,下次某个时候继续恢复执行该goroutine 11 | fmt.Println(word) 12 | } 13 | } 14 | 15 | func main() { 16 | /* 17 | 默认情况下,调度器仅使用单线程,也就是说只实现了并发。 18 | 想要发挥多核处理器的并行, 19 | 需要在我们的程序中显示调用 runtime.GOMAXPROCS(n)告诉调度器同时使用多个线程。 20 | GOMAXPROCS设置了同时运行逻辑代码的系统线程的最大数量,并返回之前的设置。 21 | 如果n < 1,不会改变当前设置 22 | */ 23 | // 都次运行 返回的结果都不一样 24 | go say("afterloe") // 异步执行 25 | say("joe") // 当前 执行 26 | } 27 | -------------------------------------------------------------------------------- /3-learn/4-test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // 参数接收的时候也是一样 6 | func sum(a []int, c chan int) { 7 | sum := 0 8 | for _, v := range a { 9 | sum += v 10 | } 11 | // 通过操作符<-来接收和发送数据。 12 | c <- sum // send sum to c 13 | } 14 | 15 | func main() { 16 | a := []int{1, 2, 3, 4, 5, 6, 7, 8, 9} 17 | // chan 发送或者接收值。这些值只能是特定的类型:channel类型。 18 | // 定义一个channel时,也需要定义发送到channel的值的类型。 19 | // 必须使用make创建channel。 20 | c := make(chan int) 21 | go sum(a[:len(a)/2], c) // 数组后一半 22 | go sum(a[len(a)/2:], c) // 数组前一半 ':' 截断操作 23 | // channel接收和发送数据都是阻塞的 无缓冲channel是在多个goroutine之间同步很棒的工具 24 | x, y := <- c, <- c // return from c 通过操作符<-来接收和发送数据。 25 | fmt.Println(x, y, x + y) 26 | 27 | // 申明一个buffered Channels 28 | c2 := make(chan int, 3) // value == 0 无缓冲 柱塞,value > 0 缓冲,直到填满value 29 | // 这个channel中,前3个元素可以无阻塞的写入。 30 | // 当写入第4个 元素时,代码将会阻塞,直到其他goroutine从channel中读取一些元素才能放入 31 | c2 <- 1 32 | fmt.Println(<- c2) 33 | c2 <- 2 34 | c2 <- 3 35 | c2 <- 4 36 | fmt.Println(<- c2) 37 | fmt.Println(<- c2) 38 | fmt.Println(<- c2) 39 | //fmt.Println(<- c2) // 超出返回是会报错的 all goroutines are asleep - deadlock! 40 | } 41 | -------------------------------------------------------------------------------- /3-learn/5-test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | "runtime" 7 | ) 8 | 9 | func fibonacci (n int, c chan int) { 10 | x, y:= 1, 1 11 | for i := 0; i < n; i++ { 12 | c <- x 13 | x, y = y, x + y 14 | } 15 | close(c) // 显式的 关闭chan 16 | // 在生产者的地方关闭channel 17 | } 18 | 19 | func seance(c, quit chan int) { 20 | x, y := 1, 1 21 | // select不再阻塞等待channel 所以需要死循环 22 | for { 23 | // select其实就是类似switch的功能 24 | select { 25 | case c <- x: 26 | x, y = y, x + y 27 | // 有时候goroutime会有阻塞的时候,可以使用select来设置超时操作 28 | case <- time.After(5 * time.Second): 29 | fmt.Println("time out") 30 | break 31 | case <- quit: 32 | fmt.Println("quit") 33 | return 34 | // 在select里面还有default语法,default就是当监听的channel都没有准备好的时候,默认执行的 35 | } 36 | } 37 | } 38 | 39 | func main() { 40 | c := make(chan int, 12) // 需要指定长度 41 | go fibonacci(cap(c), c) 42 | if _, ok := <- c; ok { // 43 | fmt.Println("channel 没有关闭") 44 | } 45 | // rang c 能够不断地读取channel里的数据,直到该channel被显示的关闭 46 | for i := range c { 47 | fmt.Println(i) 48 | } 49 | 50 | // 当 channel 关闭的时候是不能在向其发送数据的 51 | b, quit := make(chan int), make(chan int) 52 | // 异步执行匿名函数 53 | go func() { 54 | for i := 0; i< 10; i++ { 55 | fmt.Println(<-b) 56 | } 57 | quit <- 0 58 | runtime.Goexit() // 退出当前执行的goroutine 59 | }() 60 | seance(b, quit) 61 | 62 | fmt.Println(runtime.NumCPU()) // 输出CPU核数 63 | fmt.Println(runtime.NumGoroutine()) // 输出正在执行和排队的任务总数 64 | } 65 | -------------------------------------------------------------------------------- /3-learn/summary.md: -------------------------------------------------------------------------------- 1 | # 第三天 2 | 3 | interface 和 struct 一样是能够进行循环嵌入的。嵌入的方式如下 4 | ```golang 5 | type vo1 interface { 6 | equal() 7 | } 8 | 9 | // 嵌入式 interface 理解为 interface 的继承 10 | type vo2 interface { 11 | vo1 12 | say(vo1) string // 也可以使用 函数传参的形式 13 | } 14 | ``` 15 | 使用的时候,和普通的使用方式是一样的,所以具有和前的扩展性[代码在这](./Main.go) 16 | 17 | 反射是使用`reflect`包,使用方法`ValueOf`和`TypeOf`来反射获取数据的类型和值,当然要求就是被反射的值是能够修改的。interface的值是不能进行修改的,会报错 18 | 修改值的方法如下 19 | ```golang 20 | p := reflect.ValueOf(&x) 21 | v := p.Elem() 22 | v.SetFloat(3.14) 23 | //v.SetString("afterloe") // 类型必须是一致的才可以修改 24 | getType(x) 25 | ``` 26 | [代码在这](./Main1.go) 27 | 28 | 使用`runtime`可以进行go的并发调用[代码在这](./Main2.go) 使用`go`关键字,如果要运用多核的话,需要使用`GOMAXPROCS`来设置同时运行的系统线程最大数量 29 | 30 | 多核或者多并发可以使用`channel`来实现夸线程通讯,使用关键字`chan`来执行通知的管道流,而且channel可以指定是使用普通还是缓冲模式[代码在这](./Main3.go),当然也可以使用`select`来选择的输入channel或来设置timeout同时还有匿名函数的使用[代码在这](./Main4.go) 他的高阶使用可以再项目中进一步的开发 31 | -------------------------------------------------------------------------------- /4-learn/Main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "./fs" // 导入函数 会调用 该包的 init 函数 5 | "fmt" 6 | "strconv" // 工具类 转换 bool 转换为 string 7 | "reflect" 8 | ) 9 | 10 | func main() { 11 | fs.ReadFile("../info") // 调用方法 12 | //fs.sayHello() go 约定 大写开头的就是 public 小写开头的 就是 private 13 | fmt.Println("export value is " + strconv.Itoa(fs.MIN_LINE)) // 获取 fs 导出的变量 14 | 15 | // 调用包里的模块 16 | info, err := fs.ReadRealFile("/Users/afterloe/Afterloe/go/golangStudy", ".gitignore") 17 | // 如果存在error 就输出 18 | if nil != err { 19 | fmt.Println("find err") 20 | fmt.Println(err.Error()) // 输出error 21 | return 22 | } 23 | 24 | fmt.Println(info) 25 | 26 | flag, err := fs.WriteRealFile([]byte("i need you"), "test") // 往 /tmp/test 文件中写入一句话 27 | if err != nil { 28 | fmt.Println("find error") 29 | fmt.Println(err.Error()) 30 | } 31 | 32 | fmt.Println("write file " + strconv.FormatBool(flag)) 33 | 34 | user := map[string]interface{}{"name": "afterloe", "sex": 0, "domain": [...]string{"go", "java", "cpp"}} 35 | userStr, error := fs.FormatToString(&user) // 获取user转换后的 json文本 36 | if nil != error { 37 | fmt.Println(error) 38 | } 39 | fmt.Println(userStr) 40 | 41 | // 读取本地文件中的json文件 42 | json, _ := fs.ReadRealFile("/Users/afterloe/Afterloe/node/cynomy_axure", "package.json") 43 | obj, _ := fs.FormatToStruct(&json) // 文件转换为 vol interface {} 的实例 44 | 45 | //depMap := obj["dependencies"].(map[string]interface {}) // 可以直接使用 获取的也是 vol interface {} 46 | depMap := obj["dependencies"] // 可以直接使用 获取的也是 vol interface {} 47 | fmt.Println(obj["name"].(string)) // 按照string 进行处理 48 | fmt.Println(obj["age"].(float64)) // 转换为int 进行处理 49 | v := reflect.ValueOf(depMap) // 使用反射 获取 map 50 | 51 | // 获取map中的指定值 52 | // fmt.Println(depMap["koa"]) 53 | fmt.Println(reflect.TypeOf(depMap)) // map[string]interface {} 54 | fmt.Println(v.MapIndex(reflect.ValueOf("koa"))) // 使用这个方式来直接取map 中 koa的值 55 | 56 | // index 第一个参数是 下标 -- 循环输出map中的值 57 | for _, key := range v.MapKeys() { 58 | val := v.MapIndex(key) 59 | fmt.Printf("%s -> %s \n", key.Interface(), val.Interface()) 60 | } 61 | 62 | // 循环输出 slice 中的值 63 | keywords := obj["keywords"].([]interface{}) 64 | fmt.Println(reflect.TypeOf(keywords)) 65 | v = reflect.ValueOf(keywords) 66 | fmt.Println(v.Index(0)) // 直接取第一个数的值 67 | for _, val := range keywords{ 68 | val := reflect.ValueOf(val) 69 | fmt.Println(val.Interface()) 70 | } 71 | } -------------------------------------------------------------------------------- /4-learn/fs/Error.go: -------------------------------------------------------------------------------- 1 | package fs 2 | 3 | import "fmt" 4 | 5 | // 自定义 Error 6 | type Error struct { 7 | msg string 8 | code int 9 | } 10 | 11 | // 自定义异常要实现这个方法 12 | func (e *Error) Error() string { 13 | return fmt.Sprintf("%d - %s", e.code, e.msg) 14 | } 15 | -------------------------------------------------------------------------------- /4-learn/fs/FormatJSON.go: -------------------------------------------------------------------------------- 1 | package fs 2 | 3 | import ( 4 | "encoding/json" 5 | ) 6 | 7 | func FormatToStruct(chunk *string) (map[string]interface{}, error){ 8 | rep := make(map[string]interface{}) 9 | err := json.Unmarshal([]byte(*chunk), &rep) // 使用这种方式来转换复杂json 10 | // slcB, _ := json.Marshal(slcD) // 这种方式可以直接转换 boolean、int、float、string、slice、map 11 | if nil != err { 12 | return nil, &Error{"json format error", 500} 13 | } 14 | return rep, nil 15 | } 16 | 17 | func FormatToString(vol interface{}) (string, error){ 18 | buf, err := json.Marshal(vol) 19 | if nil != err { 20 | return "", &Error{"format object error", 500} 21 | } 22 | return string(buf),nil 23 | } -------------------------------------------------------------------------------- /4-learn/fs/Fs.go: -------------------------------------------------------------------------------- 1 | package fs 2 | 3 | import "fmt" 4 | 5 | var MIN_LINE int 6 | 7 | func ReadFile(name string) string{ 8 | fmt.Printf("try to read file -> %s \n", name) 9 | 10 | return "done" 11 | } 12 | 13 | func sayHello() { 14 | fmt.Println("say a word") 15 | } 16 | 17 | func init() { 18 | MIN_LINE = 123 19 | sayHello() 20 | } -------------------------------------------------------------------------------- /4-learn/fs/ReadFile.go: -------------------------------------------------------------------------------- 1 | package fs 2 | 3 | import ( 4 | "path/filepath" 5 | "io/ioutil" 6 | ) 7 | 8 | const ( 9 | BUFFER_SIZE = 64 * 1024 10 | ) 11 | 12 | func ReadRealFile(path,name string) (string, error) { 13 | // 读取文件 14 | data, err := ioutil.ReadFile(filepath.Join(path, name)) 15 | if nil != err { 16 | return "", &Error{"no such this file", 500} 17 | } 18 | 19 | // 将 byte 转换为 string 20 | return string(data), nil 21 | } -------------------------------------------------------------------------------- /4-learn/fs/WriteFile.go: -------------------------------------------------------------------------------- 1 | package fs 2 | 3 | import ( 4 | "io/ioutil" 5 | "path/filepath" 6 | ) 7 | 8 | const ( 9 | PARENT = "/tmp" 10 | MODULE = 0755 11 | ) 12 | 13 | func WriteRealFile(buf []byte, name string) (bool, error) { 14 | err := ioutil.WriteFile(filepath.Join(PARENT, name), buf, MODULE) 15 | if nil != err { 16 | return false, &Error{"can't write file.", 500} 17 | } 18 | 19 | return true, nil 20 | } 21 | -------------------------------------------------------------------------------- /4-learn/summary.md: -------------------------------------------------------------------------------- 1 | # 第四天 2 | 3 | 分包 通过 `import` 关键字来引入本地包[代码](./Main.go) 4 | 5 | ```golang 6 | import ( 7 | "./fs" // 引入本地fs包 8 | ) 9 | ``` 10 | [fs源码](./fs/Fs.go) 11 | 12 | 切记一点,小写开头的是private的,大写开头的是public的。使用`strconv`这个工具包可以经常转换比如数字转字符串、boolean转字符串。而且需要注意的是,在同一个包内是可以不用引入直接调用的,并且不能出现同名的函数。尤其是大写开头的 13 | 14 | 在go里面,函数返回一般会带一个error 15 | ```golang 16 | func t() (string, error) {} 17 | ``` 18 | 19 | 自定义error是支持的。不过需要重写`Error()`方法。[代码在这](./fs/Error.go) 20 | 21 | ```golang 22 | // 自定义 Error 23 | type Error struct { 24 | msg string 25 | code int 26 | } 27 | 28 | // 自定义异常要实现这个方法 29 | func (e *Error) Error() string { 30 | return fmt.Sprintf("%d - %s", e.code, e.msg) 31 | } 32 | ``` 33 | 34 | 使用`io/ioutil`可以读取文件[代码](./fs/ReadFile.go) 写文件也可以这个工具[代码](./fs/WriteFile.go) 35 | 转换JSON可以使用`encoding/json`工具包,具体使用可以[参考这里](./fs/FormatJSON.go) 36 | 37 | 反射在这里使用的很高详细的[这里](./Main.go),使用`reflect`包就可以,`interface{}`转`map`的方法如下 38 | ```golang 39 | v := reflect.ValueOf(depMap) // 使用反射 获取 map 40 | 41 | // 获取map中的指定值 42 | // fmt.Println(depMap["koa"]) 43 | fmt.Println(reflect.TypeOf(depMap)) // map[string]interface {} 44 | fmt.Println(v.MapIndex(reflect.ValueOf("koa"))) // 使用这个方式来直接取map 中 koa的值 45 | 46 | // index 第一个参数是 下标 -- 循环输出map中的值 47 | for _, key := range v.MapKeys() { 48 | val := v.MapIndex(key) 49 | fmt.Printf("%s -> %s \n", key.Interface(), val.Interface()) 50 | } 51 | 52 | ``` 53 | 54 | `interface{}`转`slice`方法如下 55 | 56 | ```golang 57 | // 循环输出 slice 中的值 58 | keywords := obj["keywords"].([]interface{}) 59 | fmt.Println(reflect.TypeOf(keywords)) 60 | v = reflect.ValueOf(keywords) 61 | fmt.Println(v.Index(0)) // 直接取第一个数的值 62 | for _, val := range keywords{ 63 | val := reflect.ValueOf(val) 64 | fmt.Println(val.Interface()) 65 | } 66 | ``` 67 | -------------------------------------------------------------------------------- /5-learn/DBUtil/ConnectionInfo.go: -------------------------------------------------------------------------------- 1 | package DBUtil 2 | 3 | import "fmt" 4 | 5 | // 定义链接信息结构体 6 | type connectInfo struct { 7 | Uname string 8 | DbName string 9 | Pwd string 10 | Host string 11 | } 12 | 13 | // 饿汉 模式 14 | var info interface{} 15 | 16 | // 重写 toString 方法,让他能够输出 描写的信息 17 | func (info *connectInfo) String() string { 18 | return fmt.Sprintf("{Uname: %s, DbName: %s, Pwd: %s, Host: %s}", 19 | info.Uname, info.DbName, info.Pwd, info.Host) 20 | } 21 | 22 | // 初始化链接参数 如果已经初始化 则可以重新设置这个参数 23 | func InitConnectionInfo(uname, dbname, pwd, host string) { 24 | if val, ok := info.(*connectInfo); !ok { 25 | info = &connectInfo{uname, dbname, pwd, host} 26 | return 27 | } else { 28 | val.Uname = uname 29 | val.DbName = dbname 30 | val.Pwd = pwd 31 | val.Host = host 32 | } 33 | } 34 | 35 | // 输出全局设置的 链接信息 36 | func getConnectionInfo() *connectInfo { 37 | if val, ok := info.(*connectInfo); ok { 38 | return val 39 | } else { 40 | return nil 41 | } 42 | } -------------------------------------------------------------------------------- /5-learn/DBUtil/Error.go: -------------------------------------------------------------------------------- 1 | package DBUtil 2 | 3 | import "fmt" 4 | 5 | // 自定义异常输出 6 | type Error struct { 7 | msg string 8 | } 9 | 10 | func (e *Error) Error() string { 11 | return fmt.Sprintf("has error -> %s", e.msg) 12 | } -------------------------------------------------------------------------------- /5-learn/DBUtil/GetConn.go: -------------------------------------------------------------------------------- 1 | package DBUtil 2 | 3 | import ( 4 | "database/sql" 5 | _ "github.com/lib/pq" 6 | "fmt" 7 | "text/template" 8 | "bytes" 9 | ) 10 | 11 | // 设置 sql 链接模板 12 | var sqlConnectionTemplate *template.Template 13 | var sqlConnectionStr string 14 | 15 | func init() { 16 | // 初始化模板参数 17 | templatePoint, _ := template.New("db"). 18 | Parse("user={{.Uname}} dbname={{.DbName}} password={{.Pwd}} host={{.Host}} sslmode=disable") 19 | sqlConnectionTemplate = templatePoint 20 | sqlConnectionStr = "" 21 | } 22 | 23 | type breakthroughPoint interface { 24 | execute(*sql.DB) (interface{}, error) 25 | } 26 | 27 | func UseConnection(point breakthroughPoint) (interface{}, error) { 28 | db, error := openConnection() 29 | if nil != error { 30 | return nil, &Error{error.Error()} 31 | } 32 | rest, err := point.execute(db) 33 | defer db.Close() 34 | return rest, err 35 | } 36 | 37 | func openConnection()(*sql.DB, error) { 38 | if "" == sqlConnectionStr { 39 | if nil == sqlConnectionTemplate { 40 | return nil, &Error{"初始化失败"} 41 | } 42 | connectInfo := getConnectionInfo() 43 | var tpl bytes.Buffer 44 | if err := sqlConnectionTemplate.ExecuteTemplate(&tpl, "db", connectInfo); err != nil { 45 | fmt.Println(err.Error()) 46 | return nil, &Error{"sql转换失败"} 47 | } 48 | 49 | sqlConnectionStr = tpl.String() 50 | } 51 | return sql.Open("postgres", sqlConnectionStr) 52 | } -------------------------------------------------------------------------------- /5-learn/DBUtil/Insert.go: -------------------------------------------------------------------------------- 1 | package DBUtil 2 | 3 | import ( 4 | "database/sql" 5 | "reflect" 6 | ) 7 | 8 | type insertExecute struct { 9 | sql string 10 | domainList []interface{} 11 | } 12 | 13 | func mapToValueList(m interface{}) []interface{} { 14 | val := reflect.ValueOf(m) 15 | valueList := make([]interface{}, 0) 16 | for i := 0; i < val.Len(); i++ { 17 | valueList = append(valueList, val.Index(i).Interface()) 18 | } 19 | return valueList 20 | } 21 | 22 | func (insert *insertExecute) execute(db *sql.DB) (interface{}, error) { 23 | stmt, error := db.Prepare(insert.sql) 24 | if nil != error { 25 | return nil, error 26 | } 27 | insertHistory := make([]interface{}, 0) 28 | for _, domain := range insert.domainList { 29 | if res, err := stmt.Exec(mapToValueList(domain)...); nil != err { 30 | return nil, &Error{err.Error()} 31 | } else { 32 | insertHistory = append(insertHistory, res) 33 | } 34 | } 35 | 36 | return insertHistory, nil 37 | } 38 | 39 | func InsertMap(sql string, domainList []interface{}) ([]interface{}, error) { 40 | var point breakthroughPoint 41 | point = &insertExecute{sql, domainList} 42 | result, err := UseConnection(point) 43 | if nil != err { 44 | return nil, &Error{err.Error()} 45 | } 46 | 47 | return result.([]interface{}), nil 48 | } 49 | -------------------------------------------------------------------------------- /5-learn/DBUtil/Query.go: -------------------------------------------------------------------------------- 1 | package DBUtil 2 | 3 | import ( 4 | "database/sql" 5 | ) 6 | 7 | type queryExecute struct { 8 | sql string 9 | args []interface{} 10 | } 11 | 12 | func (query *queryExecute) rowsToMap(rows *sql.Rows) ([]map[string]interface{}, error) { 13 | cols, _ := rows.Columns() 14 | result := make([]map[string]interface{}, 0) 15 | columns := make([]interface{}, len(cols)) 16 | columnPointers := make([]interface{}, len(cols)) 17 | for i := range columns { 18 | columnPointers[i] = &columns[i] 19 | } 20 | for rows.Next() { 21 | if err := rows.Scan(columnPointers...); err != nil { 22 | return nil, &Error{err.Error()} 23 | } 24 | 25 | m := make(map[string]interface{}) 26 | for i, colName := range cols { 27 | val := columnPointers[i].(*interface{}) 28 | m[colName] = *val 29 | } 30 | result = append(result, m) 31 | } 32 | 33 | return result, nil 34 | } 35 | 36 | func (query *queryExecute) execute(db *sql.DB) (interface{}, error) { 37 | rows, err := db.Query(query.sql, query.args...) 38 | if nil != err { 39 | return nil, &Error{err.Error()} 40 | } 41 | return query.rowsToMap(rows) 42 | } 43 | 44 | // 传入查询的sql 返回 结果集组成的map结果 45 | func QueryInfo(sql string, args ...interface{})([]map[string]interface{}, error){ 46 | var point breakthroughPoint 47 | point = &queryExecute{sql, args} 48 | result, err := UseConnection(point) 49 | if nil != err { 50 | return nil, &Error{err.Error()} 51 | } 52 | return result.([]map[string]interface{}), nil 53 | } 54 | -------------------------------------------------------------------------------- /5-learn/DBUtil/Update.go: -------------------------------------------------------------------------------- 1 | package DBUtil 2 | 3 | import ( 4 | "database/sql" 5 | "reflect" 6 | ) 7 | 8 | type updateExecute struct { 9 | sql string 10 | domainList []interface{} 11 | } 12 | 13 | func checkRes(updateHistory []interface{}) (interface{}, error) { 14 | for _, update := range updateHistory { 15 | val := reflect.ValueOf(update) 16 | val.MethodByName("RowsAffected").Call(nil) 17 | } 18 | 19 | return true, nil 20 | } 21 | 22 | func (update *updateExecute) execute(db *sql.DB) (interface{}, error) { 23 | stmt, error := db.Prepare(update.sql) 24 | if nil != error { 25 | return nil, &Error{error.Error()} 26 | } 27 | updateHistory := make([]interface{}, 0) 28 | for _, domain := range update.domainList { 29 | if res, err := stmt.Exec(mapToValueList(domain)...); nil != err { 30 | return nil, &Error{err.Error()} 31 | } else { 32 | updateHistory = append(updateHistory, res) 33 | } 34 | } 35 | 36 | return checkRes(updateHistory) 37 | } 38 | 39 | func UpdateMap(sql string, domainList []interface{}) (bool, error) { 40 | var point breakthroughPoint 41 | point = &updateExecute{sql, domainList} 42 | result, err := UseConnection(point) 43 | if nil != err { 44 | return false, &Error{err.Error()} 45 | } 46 | 47 | return result.(bool), nil 48 | } 49 | -------------------------------------------------------------------------------- /5-learn/Main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "./DBUtil" 6 | "reflect" 7 | ) 8 | 9 | func main() { 10 | // 初始化链接 11 | DBUtil.InitConnectionInfo("centos", "addressengine", "user123", "kini") 12 | // 调用查询方法 -- 封装过 返回 map对象 13 | mapResult, err := DBUtil.QueryInfo("SELECT * FROM address_engine LIMIT $1", 10) 14 | if nil != err { 15 | fmt.Println(err.Error()) 16 | return 17 | } 18 | for _, m := range mapResult { 19 | fmt.Println(m) 20 | } 21 | 22 | insertSQL := "INSERT INTO public.address_engine(id, address, area_id, area_name, community_id," + 23 | " community_name, gid, latitude, longitude, num, street_id, street_name) " + 24 | "VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12) returning id" 25 | 26 | insertResult, err := DBUtil.InsertMap(insertSQL, []interface{}{ 27 | []interface{}{202891, "福建省福州市鼓楼区中军后6号7座806单元 - t4", "350104", "仓山区", "350102004001", "军门社区", "136376", nil, nil, nil, "350102004000", "东街街道"}, 28 | []interface{}{202892, "福建省福州市鼓楼区中军后6号7座806单元 - t5", "350104", "仓山区", "350102004001", "军门社区", "136376", nil, nil, nil, "350102004000", "东街街道"}, 29 | []interface{}{202893, "福建省福州市鼓楼区中军后6号7座806单元 - t6", "350104", "仓山区", "350102004001", "军门社区", "136376", nil, nil, nil, "350102004000", "东街街道"}, 30 | []interface{}{202894, "福建省福州市鼓楼区中军后6号7座806单元 - t7", "350104", "仓山区", "350102004001", "军门社区", "136376", nil, nil, nil, "350102004000", "东街街道"}, 31 | }) 32 | if nil != err { 33 | fmt.Println(err.Error()) 34 | return 35 | } 36 | for _, id := range insertResult { 37 | val := reflect.ValueOf(id) 38 | fmt.Println(val.MethodByName("LastInsertId").Call(nil)[0].Interface()) 39 | } 40 | 41 | updateSQL := "UPDATE public.address_engine set address = $1 WHERE id = $2" 42 | update, err := DBUtil.UpdateMap(updateSQL, []interface{}{ 43 | []interface{}{"测试地址", 202891}, 44 | []interface{}{"测试地址", 202892}, 45 | []interface{}{"测试地址", 202893}, 46 | []interface{}{"测试地址", 202894}, 47 | }) 48 | if nil != err { 49 | fmt.Println(err.Error()) 50 | return 51 | } 52 | fmt.Println(update) 53 | 54 | deleteSQL := "DELETE FROM public.address_engine WHERE id = $1" 55 | del, err := DBUtil.UpdateMap(deleteSQL, []interface{}{ 56 | []interface{}{202891}, 57 | []interface{}{202892}, 58 | []interface{}{202893}, 59 | []interface{}{202894}, 60 | }) 61 | if nil != err { 62 | fmt.Println(err.Error()) 63 | return 64 | } 65 | fmt.Println(del) 66 | 67 | } 68 | -------------------------------------------------------------------------------- /5-learn/day05.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Target Server Type : PostgreSQL 3 | Target Server Version : 90405 4 | File Encoding : 65001 5 | 6 | Date: 28/02/2018 15:39:14 7 | */ 8 | 9 | 10 | -- ---------------------------- 11 | -- Table structure for administrative_area 12 | -- ---------------------------- 13 | DROP TABLE IF EXISTS "administrative_area"; 14 | CREATE TABLE "administrative_area" ( 15 | "id" int8 NOT NULL DEFAULT NULL, 16 | "level" int8 DEFAULT NULL, 17 | "basename" varchar(255) COLLATE "pg_catalog"."default" DEFAULT NULL::character varying, 18 | "code" varchar(255) COLLATE "pg_catalog"."default" DEFAULT NULL::character varying, 19 | "name" varchar(255) COLLATE "pg_catalog"."default" DEFAULT NULL::character varying, 20 | "parent_id" varchar(255) COLLATE "pg_catalog"."default" DEFAULT NULL::character varying 21 | ) 22 | ; 23 | ALTER TABLE "administrative_area" OWNER TO "ascs"; 24 | 25 | -- ---------------------------- 26 | -- Primary Key structure for table administrative_area 27 | -- ---------------------------- 28 | ALTER TABLE "administrative_area" ADD CONSTRAINT "administrative_area_pkey" PRIMARY KEY ("id"); 29 | -------------------------------------------------------------------------------- /5-learn/summary.md: -------------------------------------------------------------------------------- 1 | # 第五天 2 | 3 | 数据在实验环境下使用的连接包是`github.com/lib/pq` 这是用Postgres作为数据库的驱动。官方文档在[这里](https://godoc.org/github.com/lib/pq)。 就使用来说还是很方便的具体可以参考官方给出的例子 4 | ```golang 5 | import ( 6 | "database/sql" 7 | _ "github.com/lib/pq" 8 | } 9 | 10 | func main() { 11 | connStr := "user=pqgotest dbname=pqgotest sslmode=verify-full" 12 | db, err := sql.Open("postgres", connStr) 13 | if err != nil { 14 | log.Fatal(err) 15 | } 16 | 17 | age := 21 18 | rows, err := db.Query("SELECT name FROM users WHERE age = $1", age) 19 | … 20 | } 21 | ``` 22 | 23 | 在第五天的数据库实践里,更多的是结合之前几天的内容进行综合的联系。第一个就是go里面的分包用法*大写公有,小写私有*。 自定义 Error的用法 *重写Error() string* 方法。其实官方提供的异常处理可以参考[这里](https://blog.golang.org/error-handling-and-go) 24 | 25 | ```golang 26 | type Error struct { 27 | msg string 28 | } 29 | 30 | func (e *Error) Error() string { 31 | return fmt.Sprintf("has error -> %s", e.msg) 32 | } 33 | ``` 34 | 35 | 在[getConnectionInfo](./DBUtil/ConnectionInfo.go)里用到一个单例模式,这里需要用到数据库的链接信息,这个消息在工具初始化的时候才实例化的,使用的时候需要先使用`InitConnectionInfo`方法来初始化数据库连接信息。然后使用自己定义的结构体来存储数据库连接信息 36 | 37 | ```golang 38 | type connectInfo struct { 39 | Uname string 40 | DbName string 41 | Pwd string 42 | Host string 43 | } 44 | ``` 45 | 然后是一个[核心工具类](./DBUtil/GetConn.go) 这里面封装了上面说到的工具`pq`,使用起来也是很方便的。定义SQL连接信息模板。这里使用的就是刚刚写到的链接细腻的结构体,这里使用到的是`text/template`这个工具类 46 | 47 | ```golang 48 | templatePoint, _ := template.New("db"). 49 | Parse("user={{.Uname}} dbname={{.DbName}} password={{.Pwd}} host={{.Host}} sslmode=disable") 50 | var tpl bytes.Buffer 51 | if err := sqlConnectionTemplate.ExecuteTemplate(&tpl, "db", connectInfo); err != nil { 52 | fmt.Println(err.Error()) 53 | return nil, &Error{"sql转换失败"} 54 | } 55 | 56 | sqlConnectionStr = tpl.String() 57 | ``` 58 | 这里使用的是模板的`ExecuteTemplate`方法,这里面需要一个数据流,这里可以使用`bytes.Buffer` 来接收,然后用他的`地址`来存储流中的信息,最后在使用`String()`方法来输出结果。这样就可以接收输入输出流 59 | 然后封装一个`inteface` 来实现aop功能,实现通用业务封装 60 | 61 | ```golang 62 | type breakthroughPoint interface { 63 | execute(*sql.DB) (interface{}, error) 64 | } 65 | 66 | func UseConnection(point breakthroughPoint) (interface{}, error) { 67 | db, error := openConnection() 68 | if nil != error { 69 | return nil, &Error{error.Error()} 70 | } 71 | rest, err := point.execute(db) 72 | defer db.Close() 73 | return rest, err 74 | } 75 | ``` 76 | 77 | 然后每个类只要使用`UseConnection()`方法,就能够实现数据db获取,也可用于并发使用。 78 | 79 | 接下来就是数据库的CRUD四种操作,优先Query吧,这里封装了一个方法,传入sql返回map结果,可以用于日常使用代码可以查看[这里](./DBUtil/Query.go) 核心是这里 80 | 81 | ```golang 82 | func (query *queryExecute) rowsToMap(rows *sql.Rows) ([]map[string]interface{}, error) { 83 | cols, _ := rows.Columns() 84 | result := make([]map[string]interface{}, 0) 85 | columns := make([]interface{}, len(cols)) 86 | columnPointers := make([]interface{}, len(cols)) 87 | for i := range columns { 88 | columnPointers[i] = &columns[i] 89 | } 90 | for rows.Next() { 91 | if err := rows.Scan(columnPointers...); err != nil { 92 | return nil, &Error{err.Error()} 93 | } 94 | 95 | m := make(map[string]interface{}) 96 | for i, colName := range cols { 97 | val := columnPointers[i].(*interface{}) 98 | m[colName] = *val 99 | } 100 | result = append(result, m) 101 | } 102 | 103 | return result, nil 104 | } 105 | ``` 106 | 107 | 解析一下 108 | * 查询的记过返回的是一个rows的指针,可以调用`Columns`方法获取返回的列头 109 | * 定义两个slice,一个slice用户存储列的数据,一个slice来存储对应的指针 110 | * 给指针数组赋值 111 | * 循环返回结果,每次调用`Scan`方法,传入指针slice,使用`...`来实现 可变参数传入 112 | * 将获取的数据转换到map之中 113 | * 把每次的map通过`append`方法加入的返回结果集 即可 114 | 115 | 插入的代码和查询的类型,可以参看[这里](./DBUtil/Insert.go) 116 | 117 | 修改和删除的代码可以用同一段可以参看[这里](./DBUtil/Update.go) 不过需要注意的就是 操作之后要调用 res的方法 118 | ```golang 119 | func checkRes(updateHistory []interface{}) (interface{}, error) { 120 | for _, update := range updateHistory { 121 | val := reflect.ValueOf(update) 122 | val.MethodByName("RowsAffected").Call(nil) 123 | } 124 | 125 | return true, nil 126 | } 127 | ``` 128 | 129 | 这里是使用反射的方法调用方法来实现修改后的代码提交的。最后在[Main.go](./Main.go)里面进行CRUD的具体操作 130 | 131 | -------------------------------------------------------------------------------- /6-learn/Main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "log" 7 | //"./simpleService" 8 | "./simpleService2" 9 | ) 10 | 11 | /* 12 | 非 interface 的函数绑定,也许会更合适day05 中的 DBUtil 封装的方法 13 | 14 | func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { 15 | DefaultServeMux.HandleFunc(pattern, handler) 16 | } 17 | */ 18 | func main() { 19 | //serverListenAddr := simpleService.POINT // for simpleService - 1 20 | cynomys := &simpleService2.CustomHandler{ServerListenAddr: "127.0.0.1:8081"} // for simpleService - 2 自己定义路由表 21 | serverListenAddr := cynomys.ServerListenAddr 22 | // 设置 访问路由 23 | //http.HandleFunc("/", simpleService.Execute) // for simpleService - 1 24 | 25 | // 开始监听服务 26 | //err := http.ListenAndServe(serverListenAddr, nil) // for simpleService - 1 默认的路由表是没有404的 27 | err := http.ListenAndServe(serverListenAddr, cynomys) // for simpleService - 2 这里使用了 自己定义的路由表 28 | fmt.Println("service is success running ... in " + serverListenAddr) 29 | if nil != err { 30 | log.Fatal("Listen Port is", serverListenAddr, "Failed! because", err) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /6-learn/simpleService/SimpleService.go: -------------------------------------------------------------------------------- 1 | package simpleService 2 | 3 | import ( 4 | "net/http" 5 | "fmt" 6 | "strings" 7 | ) 8 | 9 | // 设置允许访问的域 10 | const POINT = "127.0.0.1:8082" 11 | 12 | // 路由函数 13 | func Execute(response http.ResponseWriter, request *http.Request) { 14 | // 格式化输入参数 - 默认是不会格式化的 15 | request.ParseForm() 16 | // 输出一些内容 17 | fmt.Println(request.Form) // 返回是 map[string][]interface{} 18 | fmt.Println(request.URL.Path) // 返回的是相对路径 /v1/user 19 | fmt.Println(request.URL.Scheme) 20 | // 这TM 返回的是一个 []interface{} 21 | fmt.Println(request.Form["name"]) 22 | 23 | for key, val := range request.Form { 24 | fmt.Printf("%s -> %s \n", key, strings.Join(val, " | ")) 25 | } 26 | // 向输出流写入输出内容 27 | fmt.Fprintf(response, "hello %s", request.Form["name"]) 28 | } 29 | 30 | 31 | -------------------------------------------------------------------------------- /6-learn/simpleService2/SimpleService.go: -------------------------------------------------------------------------------- 1 | package simpleService2 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "os" 7 | "io" 8 | "time" 9 | ) 10 | 11 | type CustomHandler struct { 12 | ServerListenAddr string 13 | } 14 | 15 | func UploadPic(response http.ResponseWriter, request *http.Request) { 16 | if "POST" != request.Method { 17 | response.Write([]byte("Method is not supper! Please use POST")) 18 | return 19 | } 20 | 21 | // 设置转换文件的内存大小 - 即 buffer 的大小 22 | request.ParseMultipartForm(5 << 20) // 5MB 单位是 bytes 同理 32 << 10 32kb 23 | file, handler, error := request.FormFile("icon") 24 | if error != nil { 25 | response.Write([]byte("save file find error !" + error.Error())) 26 | return 27 | } 28 | 29 | defer file.Close() 30 | fmt.Println(handler.Header) // 输出文件的请求头 31 | tf, error := os.OpenFile("/Users/afterloe/Documents/" + handler.Filename, os.O_WRONLY | os.O_CREATE, 0666) 32 | if error != nil { 33 | response.Write([]byte("save file find error !" + error.Error())) 34 | return 35 | } 36 | defer tf.Close() 37 | io.Copy(tf, file) 38 | response.Write([]byte("upload success -> " + time.Now().String())) 39 | } 40 | 41 | func Execute(response http.ResponseWriter, request *http.Request) { 42 | // 格式化输入参数 - 默认是不会格式化的 这个格式化只需要执行一次就可以了。 43 | // 在使用前格式化,多次调用时没有效果的 eg: if r.Form == nil 44 | request.ParseForm() 45 | // 输出一些内容 46 | fmt.Println(request.Form) 47 | fmt.Println(request.Method) // 请求方式 48 | 49 | /* 50 | // 一种用于多选循环的库 一般用于多选, select等表单的时候进行判断 51 | slice := []string{"afterloe", "joe"} 52 | for _, val := rang slice { 53 | if val == request.Form.Get("name") { 54 | return true 55 | } 56 | } 57 | */ 58 | fmt.Println(request.Form.Get("name")) // 也可以使用这种方式来选择 59 | 60 | // 自己写一个 函数Scheme 的校验规则 61 | if 0 == len(request.Form["name"]) { 62 | fmt.Fprintln(response, "lack parameter -> name") 63 | return 64 | } 65 | 66 | // 向输出流写入输出内容 67 | fmt.Fprintf(response, "hello %s", request.Form["name"][0]) 68 | } 69 | 70 | /* 71 | html/template有以下几个函数可以转义。用于防止html注入 72 | ● func HTMLEscape(w io.Writer, b []byte) //把b进行转义之后写到w 73 | ● func HTMLEscapeString(s string) string //转义s之后返回结果字符串 74 | ● func HTMLEscaper(args ...interface{}) string //支持多个参数一起转义,返回结果字符串 75 | */ 76 | func (*CustomHandler) ServeHTTP(response http.ResponseWriter, request *http.Request) { 77 | // 只处理 某个路径 这里可用正则来扩充 78 | // regexp.MatchString("", request.URL.Path) 正则来匹配 79 | path := request.URL.Path 80 | if path == "/" { 81 | Execute(response, request) 82 | return 83 | } else if path == "/upload" { 84 | UploadPic(response, request) 85 | return 86 | } 87 | 88 | http.NotFound(response, request) // 自己也是可以编写 404 这类的返回的 源码只是 func NotFound(w ResponseWriter, r *Request) 89 | return 90 | } 91 | -------------------------------------------------------------------------------- /6-learn/summary.md: -------------------------------------------------------------------------------- 1 | # 第六条 2 | 3 | go中是可以指定func传入的方式来控制输入输出的,例如如下代码 4 | ```golang 5 | func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { 6 | DefaultServeMux.HandleFunc(pattern, handler) 7 | } 8 | ``` 9 | 可以不是用interface就可以实现特定函数的传入,在很多地方是可以这么实用的,具体和interface的区别,我还没发现就是了。 10 | 11 | go使用http包来设置http服务,该服务可以使用自己的默认路由,启动后采用`HandleFunc`方法来设置每条路由的处理函数,默认情况下go是不会自己自动解析request中的参数的,需要手动调用`request.ParseForm()` 方法来实现解析。该方法只需要调度一次即可,多次调用是不会有什么结果的。然后`request.URL.Path`返回的是访问的相对路径,而`request.Form["name"]`可以获取输入参数为name的值,返回的是一个[]interface{}数组。所以使用的时候需要注意一下。具体代码可以参照[这里](./simpleService/SimpleService.go) 12 | 13 | 这是启动服务之后进行的postman的测试脚本如下 14 | ```sbtshell 15 | curl --request POST \ 16 | --url 'http://127.0.0.1:8082/?name=afterloe' \ 17 | --header 'Cache-Control: no-cache' \ 18 | --header 'Content-Type: application/x-www-form-urlencoded' \ 19 | --header 'Postman-Token: 57315e28-b94f-df3f-721c-127450eac187' \ 20 | --data 'name=joe&age=12' 21 | ``` 22 | 23 | ```sbtshell 24 | curl --request POST \ 25 | --url 'http://127.0.0.1:8081/v1/user?name=afterloe' \ 26 | --header 'Cache-Control: no-cache' \ 27 | --header 'Content-Type: application/x-www-form-urlencoded' \ 28 | --header 'Postman-Token: 9dc41a2e-6f9a-9f92-d3e8-a316055f9956' \ 29 | --data 'name=joe&age=12' 30 | ``` 31 | 32 | simpleService2里面不再使用默认的路由解析表了,这里采用的是自己定义的路由解析类 33 | 34 | ```golang 35 | type CustomHandler struct { 36 | ServerListenAddr string 37 | } 38 | ``` 39 | 40 | 该结构体需要实现一个接口 41 | 42 | ```golang 43 | func (*CustomHandler) ServeHTTP(response http.ResponseWriter, request *http.Request) {} 44 | ``` 45 | 46 | 在这个函数里面写入响应的路径,默认可以调用`http.NotFound(response, request)` 来输出返回,这个是可以自己定义的。 47 | 48 | 文件上传也很容易,可以使用`file, handler, error := request.FormFile("icon")` 来进行文件的转换。在使用之前需要设置缓冲区大小`request.ParseMultipartForm()` 单位是字节。然后再用`io.Copy`方法就可以了。具体代码可以参看[这里](./simpleService2/SimpleService.go) 49 | 50 | 这下面针对service2里面的服务一个postman的测试脚本 51 | 52 | ```sbtshell 53 | curl --request POST \ 54 | --url http://127.0.0.1:8081/ \ 55 | --header 'Cache-Control: no-cache' \ 56 | --header 'Content-Type: application/x-www-form-urlencoded' \ 57 | --header 'Postman-Token: 3c45ca83-d791-c3ba-9144-33dc87070627' \ 58 | --data age=12 59 | ``` 60 | 61 | ```sbtshell 62 | curl --request POST \ 63 | --url http://127.0.0.1:8081/ \ 64 | --header 'Cache-Control: no-cache' \ 65 | --header 'Content-Type: application/x-www-form-urlencoded' \ 66 | --header 'Postman-Token: 30a9abe9-a19c-db3a-484d-3b7efe6d55bf' \ 67 | --data 'name=joe&age=12' 68 | ``` 69 | 70 | 最后是安全问题 71 | 这里推荐scrypt方案,scrypt是由著名的FreeBSD黑客Colin Percival为他的备份服务Tarsnap开发的。 [库](http://code.google.com/p/go/source/browse?repo=crypto#hg%2Fscrypt) 72 | -------------------------------------------------------------------------------- /7-learn/index.md: -------------------------------------------------------------------------------- 1 | # 第七天 2 | 3 | 4 | -------------------------------------------------------------------------------- /8-learn/App_test.go: -------------------------------------------------------------------------------- 1 | package day07 2 | 3 | import "testing" 4 | 5 | // 普通测试 6 | func Test_inputInfo(t *testing.T) { 7 | str := "myName" 8 | if "afterloe" != str { 9 | t.Error("测试失败") 10 | } else { 11 | t.Log("测试通过") 12 | } 13 | } 14 | 15 | func Test_inputInfo_2(t *testing.T) { 16 | t.Error("这个还是不通过") 17 | } 18 | 19 | // 压力测试 20 | func Benchmark_Input(t *testing.B) { 21 | for i:=0; i < t.N; i++ { 22 | // do some thing 23 | } 24 | } 25 | 26 | func Benchmark_Input2(t *testing.B) { 27 | t.StopTimer() // 暂停 计数器 28 | // 初始化资源 29 | t.StartTimer() // 启动计时器 30 | for i:=0; i < t.N; i++ { 31 | // do some thing 32 | } 33 | } -------------------------------------------------------------------------------- /8-learn/Http_test.go: -------------------------------------------------------------------------------- 1 | package day07 2 | 3 | import ( 4 | "testing" 5 | "net/http" 6 | "io/ioutil" 7 | ) 8 | 9 | func Test_getHttp(t *testing.T) { 10 | response, err := http.Get("http://www.sogog.com") 11 | //http.PostForm("http://url", url.Values{"key": {"Value"}, "id": {"123"}}) 12 | //http.Post("http://url", "image/jpeg", &buf) 13 | if nil != err { 14 | t.Error("get baidu.com info fail.") 15 | } 16 | 17 | defer response.Body.Close() 18 | body, err := ioutil.ReadAll(response.Body) 19 | t.Log(string(body)) 20 | } 21 | 22 | func Test_getHttp2(t *testing.T) { 23 | request, err := http.NewRequest("GET", "http://www.baidu.com", nil) 24 | if nil != err { 25 | t.Error("get google request fail.") 26 | } 27 | client := &http.Client{} 28 | response, err := client.Do(request) 29 | if nil != err { 30 | t.Error("get google response fail.") 31 | } 32 | 33 | defer response.Body.Close() 34 | body, err := ioutil.ReadAll(response.Body) 35 | t.Log(string(body)) 36 | } -------------------------------------------------------------------------------- /8-learn/summary.md: -------------------------------------------------------------------------------- 1 | # 第八天 2 | 3 | https://github.com/cihub/seelog 4 | go语言实现的日志系统,可以参考一下 5 | 6 | 7 | 进入项目实战篇,这里打算使用go来构建一个SOA服务管理平台。需求是这样可以使用react等前后端分离的页面框架实现在页面上托拉拽来构成SOA服务。 8 | SOA这一部分就不在这里多扯了,这里主要使用web来控制docker来实现镜像拉取、部署、发布等一套活动 9 | 10 | ## docker go sdk 11 | 12 | docker的go sdk可以在[这里获取](https://docs.docker.com/develop/sdk/#install-the-sdks)到,这里使用的是go的sdk。docker的封装比较彻底,一般对外的联系都是采用`/var/run/docker.sock`来进行沟通的。所以在开发的时候需要安装docker,当然使用远程的docker也是可以的,可以修改docker的启动方式,让socket的连接方式改成tcp方式即可 13 | 14 | ### 修改远程docker为tcp连接 15 | ```sbtshell 16 | [root@localhost docker]# cd /etc/docker/ 17 | [root@localhost docker]# vim daemon.json 18 | 19 | ... 20 | [root@localhost docker]# more daemon.json 21 | 22 | { 23 | "registry-mirrors": ["https://mq8v733y.mirror.aliyuncs.com"], 24 | "hosts": ["tcp://0.0.0.0:2376", "unix:///var/run/docker.sock"] 25 | } 26 | 27 | [root@localhost docker]# firewall-cmd --zone=public --add-port=2376/tcp --permanent 28 | success 29 | [root@localhost docker]# firewall-cmd --reload 30 | success 31 | 32 | [root@localhost docker]# systemctl restart docker.service 33 | ``` 34 | 35 | 关于daemon.json 的配置文件的修改可以在[这里](https://docs.docker.com/config/daemon/#configure-the-docker-daemon)查看到。 36 | 37 | 测试一下 38 | ```sbtshell 39 | [afterloe@localhost docker]# curl 127.0.0.1:2376/v1.32/images/json 40 | 41 | [{"Containers":-1,"Created":1515547359,"Id":"sha256:d1fd7d86a8257f3404f92c4474fb3353076883062d64a09232d95d940627459d","Labels":null,"ParentId":"","RepoDigests":["registry@sha256:672d519d7fd7bbc7a448d17956ebeefe225d5eb27509d8dc5ce67ecb4a0bce54"],"RepoTags":["registry:latest"],"SharedSize":-1,"Size":33258091,"VirtualSize":33258091}] 42 | ``` 43 | 44 | 关于docker的api可以在[这里](https://docs.docker.com/engine/api/v1.32/#tag/Image) 查看到,[这里是go Doc关于docker的源码文档](https://godoc.org/github.com/docker/docker/client) 45 | 46 | 然后在go使用sdk进行访问.这里的事例是获取镜像列表的。[官网给的api示例](https://docs.docker.com/develop/sdk/examples/)是过时的,我这里用的是最新的api 47 | ```golang 48 | import ( 49 | "fmt" 50 | "github.com/docker/docker/api/types" 51 | "github.com/docker/docker/client" 52 | "golang.org/x/net/context" 53 | ) 54 | 55 | func GetImageList() interface{} { 56 | ctx := context.Background() 57 | cli, err := client.NewClientWithOpts(client.WithHost("http://yyy:2376"), 58 | client.WithVersion("1.36")) 59 | if nil != err { 60 | panic(err) 61 | } 62 | 63 | images, err := cli.ImageList(ctx, types.ImageListOptions{}) 64 | 65 | if nil != err { 66 | panic(err) 67 | } 68 | 69 | for _, image := range images { 70 | fmt.Println(image) 71 | } 72 | 73 | return images 74 | } 75 | ``` 76 | 77 | -------------------------------------------------------------------------------- /9-learn/CppGrpcClient/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.8) 2 | 3 | project(CppGrpcDemo C CXX) 4 | set(CMAKE_CXX_STANDARD 20) 5 | 6 | include("/home/afterloe/.local/grpc/examples/cpp/cmake/common.cmake") 7 | 8 | # Proto file 9 | get_filename_component(us_proto "src/proto/user_service.proto" ABSOLUTE) 10 | get_filename_component(us_proto_path "${us_proto}" PATH) 11 | 12 | # Generated sources 13 | set(us_proto_srcs "src/user_service.pb.cc") 14 | set(us_proto_hdrs "src/user_service.pb.h") 15 | set(us_grpc_srcs "src/user_service.grpc.pb.cc") 16 | set(us_grpc_hdrs "src/user_service.grpc.pb.h") 17 | 18 | # Include generated *.pb.h files 19 | include_directories("${CMAKE_CURRENT_BINARY_DIR}") 20 | 21 | # us_grpc_proto 22 | add_library(us_grpc_proto 23 | ${us_grpc_srcs} 24 | ${us_grpc_hdrs} 25 | ${us_proto_srcs} 26 | ${us_proto_hdrs}) 27 | target_link_libraries(us_grpc_proto 28 | ${_REFLECTION} 29 | ${_GRPC_GRPCPP} 30 | ${_PROTOBUF_LIBPROTOBUF}) 31 | 32 | # Targets greeter_[async_](client|server) 33 | foreach(_target main) 34 | add_executable(${_target} "src/${_target}.cpp") 35 | target_link_libraries(${_target} 36 | us_grpc_proto 37 | absl::flags 38 | absl::flags_parse 39 | ${_REFLECTION} 40 | ${_GRPC_GRPCPP} 41 | ${_PROTOBUF_LIBPROTOBUF}) 42 | endforeach() 43 | -------------------------------------------------------------------------------- /9-learn/CppGrpcClient/Makefile: -------------------------------------------------------------------------------- 1 | clean: 2 | rm -rf build 3 | 4 | proto: 5 | rm -rf src/user_service.* 6 | @protoc -I src/proto --grpc_out=src --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` src/proto/*.proto 7 | @protoc -I src/proto --cpp_out=src src/proto/*.proto 8 | echo 'generator file success, see src/*' 9 | 10 | .PHONY: proto clean 11 | -------------------------------------------------------------------------------- /9-learn/CppGrpcClient/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "user_service.grpc.pb.h" 6 | 7 | using grpc::Channel; 8 | using grpc::ClientContext; 9 | using grpc::ClientReader; 10 | using grpc::ClientReaderWriter; 11 | using grpc::ClientWriter; 12 | using grpc::Status; 13 | using Logic::UserServer; 14 | using Logic::LoginRequest; 15 | using Logic::LoginResponse; 16 | 17 | int main(int argc, char **argv) { 18 | const std::string server_host = "localhost:9092"; 19 | std::cout << "Begin to read" << server_host << std::endl; 20 | 21 | const std::unique_ptr impl = UserServer::NewStub( 22 | CreateChannel(server_host, grpc::InsecureChannelCredentials())); 23 | 24 | auto *req = new LoginRequest(); 25 | req->set_loginname("afterloe"); 26 | req->set_scrip("111111"); 27 | 28 | LoginResponse response; 29 | impl->Login(new ClientContext(), *req, &response); 30 | std::cout << "Login success: " << response.accesstoken() << " \t" << response.sessionid() << std::endl; 31 | return 0; 32 | } 33 | -------------------------------------------------------------------------------- /9-learn/CppGrpcClient/src/proto/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afterloe/golangStudy/5e895761ed4738304ddfaf930221df18ca580581/9-learn/CppGrpcClient/src/proto/.gitkeep -------------------------------------------------------------------------------- /9-learn/CppGrpcClient/src/proto/user_service.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "./;pb"; 4 | 5 | package Logic; 6 | 7 | service UserServer { 8 | rpc Login(LoginRequest) returns (LoginResponse); 9 | } 10 | 11 | message LoginRequest { 12 | string LoginName = 1; 13 | string Scrip = 2; 14 | } 15 | 16 | message LoginResponse { 17 | string SessionID = 1; 18 | string AccessToken = 2; 19 | string RefreshToken =3; 20 | } 21 | -------------------------------------------------------------------------------- /9-learn/CppGrpcClient/src/user_service.grpc.pb.cc: -------------------------------------------------------------------------------- 1 | // Generated by the gRPC C++ plugin. 2 | // If you make any local change, they will be lost. 3 | // source: user_service.proto 4 | 5 | #include "user_service.pb.h" 6 | #include "user_service.grpc.pb.h" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | namespace Logic { 23 | 24 | static const char* UserServer_method_names[] = { 25 | "/Logic.UserServer/Login", 26 | }; 27 | 28 | std::unique_ptr< UserServer::Stub> UserServer::NewStub(const std::shared_ptr< ::grpc::ChannelInterface>& channel, const ::grpc::StubOptions& options) { 29 | (void)options; 30 | std::unique_ptr< UserServer::Stub> stub(new UserServer::Stub(channel, options)); 31 | return stub; 32 | } 33 | 34 | UserServer::Stub::Stub(const std::shared_ptr< ::grpc::ChannelInterface>& channel, const ::grpc::StubOptions& options) 35 | : channel_(channel), rpcmethod_Login_(UserServer_method_names[0], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel) 36 | {} 37 | 38 | ::grpc::Status UserServer::Stub::Login(::grpc::ClientContext* context, const ::Logic::LoginRequest& request, ::Logic::LoginResponse* response) { 39 | return ::grpc::internal::BlockingUnaryCall< ::Logic::LoginRequest, ::Logic::LoginResponse, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(channel_.get(), rpcmethod_Login_, context, request, response); 40 | } 41 | 42 | void UserServer::Stub::async::Login(::grpc::ClientContext* context, const ::Logic::LoginRequest* request, ::Logic::LoginResponse* response, std::function f) { 43 | ::grpc::internal::CallbackUnaryCall< ::Logic::LoginRequest, ::Logic::LoginResponse, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(stub_->channel_.get(), stub_->rpcmethod_Login_, context, request, response, std::move(f)); 44 | } 45 | 46 | void UserServer::Stub::async::Login(::grpc::ClientContext* context, const ::Logic::LoginRequest* request, ::Logic::LoginResponse* response, ::grpc::ClientUnaryReactor* reactor) { 47 | ::grpc::internal::ClientCallbackUnaryFactory::Create< ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(stub_->channel_.get(), stub_->rpcmethod_Login_, context, request, response, reactor); 48 | } 49 | 50 | ::grpc::ClientAsyncResponseReader< ::Logic::LoginResponse>* UserServer::Stub::PrepareAsyncLoginRaw(::grpc::ClientContext* context, const ::Logic::LoginRequest& request, ::grpc::CompletionQueue* cq) { 51 | return ::grpc::internal::ClientAsyncResponseReaderHelper::Create< ::Logic::LoginResponse, ::Logic::LoginRequest, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(channel_.get(), cq, rpcmethod_Login_, context, request); 52 | } 53 | 54 | ::grpc::ClientAsyncResponseReader< ::Logic::LoginResponse>* UserServer::Stub::AsyncLoginRaw(::grpc::ClientContext* context, const ::Logic::LoginRequest& request, ::grpc::CompletionQueue* cq) { 55 | auto* result = 56 | this->PrepareAsyncLoginRaw(context, request, cq); 57 | result->StartCall(); 58 | return result; 59 | } 60 | 61 | UserServer::Service::Service() { 62 | AddMethod(new ::grpc::internal::RpcServiceMethod( 63 | UserServer_method_names[0], 64 | ::grpc::internal::RpcMethod::NORMAL_RPC, 65 | new ::grpc::internal::RpcMethodHandler< UserServer::Service, ::Logic::LoginRequest, ::Logic::LoginResponse, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>( 66 | [](UserServer::Service* service, 67 | ::grpc::ServerContext* ctx, 68 | const ::Logic::LoginRequest* req, 69 | ::Logic::LoginResponse* resp) { 70 | return service->Login(ctx, req, resp); 71 | }, this))); 72 | } 73 | 74 | UserServer::Service::~Service() { 75 | } 76 | 77 | ::grpc::Status UserServer::Service::Login(::grpc::ServerContext* context, const ::Logic::LoginRequest* request, ::Logic::LoginResponse* response) { 78 | (void) context; 79 | (void) request; 80 | (void) response; 81 | return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); 82 | } 83 | 84 | 85 | } // namespace Logic 86 | 87 | -------------------------------------------------------------------------------- /9-learn/CppGrpcClient/src/user_service.grpc.pb.h: -------------------------------------------------------------------------------- 1 | // Generated by the gRPC C++ plugin. 2 | // If you make any local change, they will be lost. 3 | // source: user_service.proto 4 | #ifndef GRPC_user_5fservice_2eproto__INCLUDED 5 | #define GRPC_user_5fservice_2eproto__INCLUDED 6 | 7 | #include "user_service.pb.h" 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | namespace Logic { 29 | 30 | class UserServer final { 31 | public: 32 | static constexpr char const* service_full_name() { 33 | return "Logic.UserServer"; 34 | } 35 | class StubInterface { 36 | public: 37 | virtual ~StubInterface() {} 38 | virtual ::grpc::Status Login(::grpc::ClientContext* context, const ::Logic::LoginRequest& request, ::Logic::LoginResponse* response) = 0; 39 | std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::Logic::LoginResponse>> AsyncLogin(::grpc::ClientContext* context, const ::Logic::LoginRequest& request, ::grpc::CompletionQueue* cq) { 40 | return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::Logic::LoginResponse>>(AsyncLoginRaw(context, request, cq)); 41 | } 42 | std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::Logic::LoginResponse>> PrepareAsyncLogin(::grpc::ClientContext* context, const ::Logic::LoginRequest& request, ::grpc::CompletionQueue* cq) { 43 | return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::Logic::LoginResponse>>(PrepareAsyncLoginRaw(context, request, cq)); 44 | } 45 | class async_interface { 46 | public: 47 | virtual ~async_interface() {} 48 | virtual void Login(::grpc::ClientContext* context, const ::Logic::LoginRequest* request, ::Logic::LoginResponse* response, std::function) = 0; 49 | virtual void Login(::grpc::ClientContext* context, const ::Logic::LoginRequest* request, ::Logic::LoginResponse* response, ::grpc::ClientUnaryReactor* reactor) = 0; 50 | }; 51 | typedef class async_interface experimental_async_interface; 52 | virtual class async_interface* async() { return nullptr; } 53 | class async_interface* experimental_async() { return async(); } 54 | private: 55 | virtual ::grpc::ClientAsyncResponseReaderInterface< ::Logic::LoginResponse>* AsyncLoginRaw(::grpc::ClientContext* context, const ::Logic::LoginRequest& request, ::grpc::CompletionQueue* cq) = 0; 56 | virtual ::grpc::ClientAsyncResponseReaderInterface< ::Logic::LoginResponse>* PrepareAsyncLoginRaw(::grpc::ClientContext* context, const ::Logic::LoginRequest& request, ::grpc::CompletionQueue* cq) = 0; 57 | }; 58 | class Stub final : public StubInterface { 59 | public: 60 | Stub(const std::shared_ptr< ::grpc::ChannelInterface>& channel, const ::grpc::StubOptions& options = ::grpc::StubOptions()); 61 | ::grpc::Status Login(::grpc::ClientContext* context, const ::Logic::LoginRequest& request, ::Logic::LoginResponse* response) override; 62 | std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::Logic::LoginResponse>> AsyncLogin(::grpc::ClientContext* context, const ::Logic::LoginRequest& request, ::grpc::CompletionQueue* cq) { 63 | return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::Logic::LoginResponse>>(AsyncLoginRaw(context, request, cq)); 64 | } 65 | std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::Logic::LoginResponse>> PrepareAsyncLogin(::grpc::ClientContext* context, const ::Logic::LoginRequest& request, ::grpc::CompletionQueue* cq) { 66 | return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::Logic::LoginResponse>>(PrepareAsyncLoginRaw(context, request, cq)); 67 | } 68 | class async final : 69 | public StubInterface::async_interface { 70 | public: 71 | void Login(::grpc::ClientContext* context, const ::Logic::LoginRequest* request, ::Logic::LoginResponse* response, std::function) override; 72 | void Login(::grpc::ClientContext* context, const ::Logic::LoginRequest* request, ::Logic::LoginResponse* response, ::grpc::ClientUnaryReactor* reactor) override; 73 | private: 74 | friend class Stub; 75 | explicit async(Stub* stub): stub_(stub) { } 76 | Stub* stub() { return stub_; } 77 | Stub* stub_; 78 | }; 79 | class async* async() override { return &async_stub_; } 80 | 81 | private: 82 | std::shared_ptr< ::grpc::ChannelInterface> channel_; 83 | class async async_stub_{this}; 84 | ::grpc::ClientAsyncResponseReader< ::Logic::LoginResponse>* AsyncLoginRaw(::grpc::ClientContext* context, const ::Logic::LoginRequest& request, ::grpc::CompletionQueue* cq) override; 85 | ::grpc::ClientAsyncResponseReader< ::Logic::LoginResponse>* PrepareAsyncLoginRaw(::grpc::ClientContext* context, const ::Logic::LoginRequest& request, ::grpc::CompletionQueue* cq) override; 86 | const ::grpc::internal::RpcMethod rpcmethod_Login_; 87 | }; 88 | static std::unique_ptr NewStub(const std::shared_ptr< ::grpc::ChannelInterface>& channel, const ::grpc::StubOptions& options = ::grpc::StubOptions()); 89 | 90 | class Service : public ::grpc::Service { 91 | public: 92 | Service(); 93 | virtual ~Service(); 94 | virtual ::grpc::Status Login(::grpc::ServerContext* context, const ::Logic::LoginRequest* request, ::Logic::LoginResponse* response); 95 | }; 96 | template 97 | class WithAsyncMethod_Login : public BaseClass { 98 | private: 99 | void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} 100 | public: 101 | WithAsyncMethod_Login() { 102 | ::grpc::Service::MarkMethodAsync(0); 103 | } 104 | ~WithAsyncMethod_Login() override { 105 | BaseClassMustBeDerivedFromService(this); 106 | } 107 | // disable synchronous version of this method 108 | ::grpc::Status Login(::grpc::ServerContext* /*context*/, const ::Logic::LoginRequest* /*request*/, ::Logic::LoginResponse* /*response*/) override { 109 | abort(); 110 | return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); 111 | } 112 | void RequestLogin(::grpc::ServerContext* context, ::Logic::LoginRequest* request, ::grpc::ServerAsyncResponseWriter< ::Logic::LoginResponse>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { 113 | ::grpc::Service::RequestAsyncUnary(0, context, request, response, new_call_cq, notification_cq, tag); 114 | } 115 | }; 116 | typedef WithAsyncMethod_Login AsyncService; 117 | template 118 | class WithCallbackMethod_Login : public BaseClass { 119 | private: 120 | void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} 121 | public: 122 | WithCallbackMethod_Login() { 123 | ::grpc::Service::MarkMethodCallback(0, 124 | new ::grpc::internal::CallbackUnaryHandler< ::Logic::LoginRequest, ::Logic::LoginResponse>( 125 | [this]( 126 | ::grpc::CallbackServerContext* context, const ::Logic::LoginRequest* request, ::Logic::LoginResponse* response) { return this->Login(context, request, response); }));} 127 | void SetMessageAllocatorFor_Login( 128 | ::grpc::MessageAllocator< ::Logic::LoginRequest, ::Logic::LoginResponse>* allocator) { 129 | ::grpc::internal::MethodHandler* const handler = ::grpc::Service::GetHandler(0); 130 | static_cast<::grpc::internal::CallbackUnaryHandler< ::Logic::LoginRequest, ::Logic::LoginResponse>*>(handler) 131 | ->SetMessageAllocator(allocator); 132 | } 133 | ~WithCallbackMethod_Login() override { 134 | BaseClassMustBeDerivedFromService(this); 135 | } 136 | // disable synchronous version of this method 137 | ::grpc::Status Login(::grpc::ServerContext* /*context*/, const ::Logic::LoginRequest* /*request*/, ::Logic::LoginResponse* /*response*/) override { 138 | abort(); 139 | return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); 140 | } 141 | virtual ::grpc::ServerUnaryReactor* Login( 142 | ::grpc::CallbackServerContext* /*context*/, const ::Logic::LoginRequest* /*request*/, ::Logic::LoginResponse* /*response*/) { return nullptr; } 143 | }; 144 | typedef WithCallbackMethod_Login CallbackService; 145 | typedef CallbackService ExperimentalCallbackService; 146 | template 147 | class WithGenericMethod_Login : public BaseClass { 148 | private: 149 | void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} 150 | public: 151 | WithGenericMethod_Login() { 152 | ::grpc::Service::MarkMethodGeneric(0); 153 | } 154 | ~WithGenericMethod_Login() override { 155 | BaseClassMustBeDerivedFromService(this); 156 | } 157 | // disable synchronous version of this method 158 | ::grpc::Status Login(::grpc::ServerContext* /*context*/, const ::Logic::LoginRequest* /*request*/, ::Logic::LoginResponse* /*response*/) override { 159 | abort(); 160 | return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); 161 | } 162 | }; 163 | template 164 | class WithRawMethod_Login : public BaseClass { 165 | private: 166 | void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} 167 | public: 168 | WithRawMethod_Login() { 169 | ::grpc::Service::MarkMethodRaw(0); 170 | } 171 | ~WithRawMethod_Login() override { 172 | BaseClassMustBeDerivedFromService(this); 173 | } 174 | // disable synchronous version of this method 175 | ::grpc::Status Login(::grpc::ServerContext* /*context*/, const ::Logic::LoginRequest* /*request*/, ::Logic::LoginResponse* /*response*/) override { 176 | abort(); 177 | return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); 178 | } 179 | void RequestLogin(::grpc::ServerContext* context, ::grpc::ByteBuffer* request, ::grpc::ServerAsyncResponseWriter< ::grpc::ByteBuffer>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { 180 | ::grpc::Service::RequestAsyncUnary(0, context, request, response, new_call_cq, notification_cq, tag); 181 | } 182 | }; 183 | template 184 | class WithRawCallbackMethod_Login : public BaseClass { 185 | private: 186 | void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} 187 | public: 188 | WithRawCallbackMethod_Login() { 189 | ::grpc::Service::MarkMethodRawCallback(0, 190 | new ::grpc::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( 191 | [this]( 192 | ::grpc::CallbackServerContext* context, const ::grpc::ByteBuffer* request, ::grpc::ByteBuffer* response) { return this->Login(context, request, response); })); 193 | } 194 | ~WithRawCallbackMethod_Login() override { 195 | BaseClassMustBeDerivedFromService(this); 196 | } 197 | // disable synchronous version of this method 198 | ::grpc::Status Login(::grpc::ServerContext* /*context*/, const ::Logic::LoginRequest* /*request*/, ::Logic::LoginResponse* /*response*/) override { 199 | abort(); 200 | return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); 201 | } 202 | virtual ::grpc::ServerUnaryReactor* Login( 203 | ::grpc::CallbackServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/) { return nullptr; } 204 | }; 205 | template 206 | class WithStreamedUnaryMethod_Login : public BaseClass { 207 | private: 208 | void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} 209 | public: 210 | WithStreamedUnaryMethod_Login() { 211 | ::grpc::Service::MarkMethodStreamed(0, 212 | new ::grpc::internal::StreamedUnaryHandler< 213 | ::Logic::LoginRequest, ::Logic::LoginResponse>( 214 | [this](::grpc::ServerContext* context, 215 | ::grpc::ServerUnaryStreamer< 216 | ::Logic::LoginRequest, ::Logic::LoginResponse>* streamer) { 217 | return this->StreamedLogin(context, 218 | streamer); 219 | })); 220 | } 221 | ~WithStreamedUnaryMethod_Login() override { 222 | BaseClassMustBeDerivedFromService(this); 223 | } 224 | // disable regular version of this method 225 | ::grpc::Status Login(::grpc::ServerContext* /*context*/, const ::Logic::LoginRequest* /*request*/, ::Logic::LoginResponse* /*response*/) override { 226 | abort(); 227 | return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); 228 | } 229 | // replace default version of method with streamed unary 230 | virtual ::grpc::Status StreamedLogin(::grpc::ServerContext* context, ::grpc::ServerUnaryStreamer< ::Logic::LoginRequest,::Logic::LoginResponse>* server_unary_streamer) = 0; 231 | }; 232 | typedef WithStreamedUnaryMethod_Login StreamedUnaryService; 233 | typedef Service SplitStreamedService; 234 | typedef WithStreamedUnaryMethod_Login StreamedService; 235 | }; 236 | 237 | } // namespace Logic 238 | 239 | 240 | #endif // GRPC_user_5fservice_2eproto__INCLUDED 241 | -------------------------------------------------------------------------------- /9-learn/Makefile: -------------------------------------------------------------------------------- 1 | proto: 2 | @rm -rf pb/*.go 3 | @protoc --proto_path=proto --go_out=pb --go_opt=paths=source_relative \ 4 | --go-grpc_out=pb --go-grpc_opt=paths=source_relative \ 5 | proto/*.proto 6 | echo 'generator pb.go success, see pb/*.go' 7 | 8 | .PHONY: proto 9 | -------------------------------------------------------------------------------- /9-learn/build_cpp_grpc_client.md: -------------------------------------------------------------------------------- 1 | 构建c++ grpc客户端 2 | === 3 | 生成过程参考[官方文档](https://grpc.org.cn/docs/languages/cpp/quickstart/) 4 | 源码位置: [CppGrpcClient](./CppGrpcClient/src/main.cpp) 5 | 6 | ## 环境准备 7 | 构建环境 8 | ```shell 9 | apt install cmake 10 | 11 | export MY_INSTALL_DIR=$HOME/.local # 选择一个目录来保存本地安装的软件包, 我用的是当前用户的家目录下的.local 12 | mkdir -p $MY_INSTALL_DIR # 创建目录 13 | export PATH="$MY_INSTALL_DIR/bin:$PATH" # 将bin目录放出去,让其能够调用grpc的一些包 14 | 15 | sudo apt install -y build-essential autoconf libtool pkg-config # 其他的一些包 16 | ``` 17 | 18 | 准备源码 19 | ```shell 20 | git clone --recurse-submodules -b v1.62.0 --depth 1 --shallow-submodules https://github.com/grpc/grpc 21 | ``` 22 | 23 | 源码构建 24 | ```shell 25 | cd grpc 26 | mkdir -p cmake/build 27 | pushd cmake/build 28 | cmake -DgRPC_INSTALL=ON \ 29 | -DgRPC_BUILD_TESTS=OFF \ 30 | -DCMAKE_INSTALL_PREFIX=$MY_INSTALL_DIR \ 31 | ../.. 32 | make -j 4 33 | make install 34 | popd 35 | ``` 36 | 37 | ## 编写client 38 | 39 | ### 引入编写好的proto文件 40 | ```shell 41 | mkdir CppGrpcDemo && cd CppGrpcDemo 42 | mkdir -p src/proto 43 | cp ${YOU_PATH_PROTO}/*.proto src/proto 44 | ``` 45 | 46 | ### 编写Makefile 47 | ```shell 48 | clean: 49 | rm -rf build 50 | 51 | proto: 52 | rm -rf src/user_service.* 53 | @protoc -I src/proto --grpc_out=src --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` src/proto/*.proto 54 | @protoc -I src/proto --cpp_out=src src/proto/*.proto 55 | echo 'generator file success, see src/*' 56 | 57 | .PHONY: proto clean 58 | ``` 59 | 执行 `make proto` 60 | 61 | ### 编写CMakeLists.txt 62 | ``` 63 | cmake_minimum_required(VERSION 3.8) 64 | 65 | project(CppGrpcDemo C CXX) 66 | set(CMAKE_CXX_STANDARD 20) 67 | 68 | include("/home/afterloe/.local/grpc/examples/cpp/cmake/common.cmake") 69 | 70 | # Proto file 71 | get_filename_component(us_proto "src/proto/user_service.proto" ABSOLUTE) 72 | get_filename_component(us_proto_path "${us_proto}" PATH) 73 | 74 | # Generated sources 75 | set(us_proto_srcs "src/user_service.pb.cc") 76 | set(us_proto_hdrs "src/user_service.pb.h") 77 | set(us_grpc_srcs "src/user_service.grpc.pb.cc") 78 | set(us_grpc_hdrs "src/user_service.grpc.pb.h") 79 | 80 | # Include generated *.pb.h files 81 | include_directories("${CMAKE_CURRENT_BINARY_DIR}") 82 | 83 | # us_grpc_proto 84 | add_library(us_grpc_proto 85 | ${us_grpc_srcs} 86 | ${us_grpc_hdrs} 87 | ${us_proto_srcs} 88 | ${us_proto_hdrs}) 89 | target_link_libraries(us_grpc_proto 90 | ${_REFLECTION} 91 | ${_GRPC_GRPCPP} 92 | ${_PROTOBUF_LIBPROTOBUF}) 93 | 94 | # Targets greeter_[async_](client|server) 95 | foreach(_target main) 96 | add_executable(${_target} "src/${_target}.cpp") 97 | target_link_libraries(${_target} 98 | us_grpc_proto 99 | absl::flags 100 | absl::flags_parse 101 | ${_REFLECTION} 102 | ${_GRPC_GRPCPP} 103 | ${_PROTOBUF_LIBPROTOBUF}) 104 | endforeach() 105 | ``` 106 | 107 | ### 编写客户端cpp文件 108 | ```c++ 109 | #include 110 | #include 111 | #include 112 | 113 | #include "user_service.grpc.pb.h" 114 | 115 | using grpc::Channel; 116 | using grpc::ClientContext; 117 | using grpc::ClientReader; 118 | using grpc::ClientReaderWriter; 119 | using grpc::ClientWriter; 120 | using grpc::Status; 121 | using Logic::UserServer; 122 | using Logic::LoginRequest; 123 | using Logic::LoginResponse; 124 | 125 | int main(int argc, char **argv) { 126 | const std::string server_host = "localhost:9092"; 127 | std::cout << "Begin to read" << server_host << std::endl; 128 | 129 | const std::unique_ptr impl = UserServer::NewStub( 130 | CreateChannel(server_host, grpc::InsecureChannelCredentials())); 131 | 132 | auto *req = new LoginRequest(); 133 | req->set_loginname("afterloe"); 134 | req->set_scrip("111111"); 135 | 136 | LoginResponse response; 137 | impl->Login(new ClientContext(), *req, &response); 138 | std::cout << "Login success: " << response.accesstoken() << " \t" << response.sessionid() << std::endl; 139 | return 0; 140 | } 141 | 142 | ``` 143 | 144 | ## 编译 145 | ```shell 146 | cd CppGrpcDemo 147 | rm -rf build && mkdir build 148 | cd build 149 | cmake .. 150 | make 151 | 152 | [ 20%] Building CXX object CMakeFiles/us_grpc_proto.dir/src/user_service.grpc.pb.cc.o 153 | [ 40%] Building CXX object CMakeFiles/us_grpc_proto.dir/src/user_service.pb.cc.o 154 | [ 60%] Linking CXX static library libus_grpc_proto.a 155 | [ 60%] Built target us_grpc_proto 156 | [ 80%] Building CXX object CMakeFiles/main.dir/src/main.cpp.o 157 | [100%] Linking CXX executable main 158 | [100%] Built target main 159 | ``` 160 | 直接运行 `./main` 即可 -------------------------------------------------------------------------------- /9-learn/go.mod: -------------------------------------------------------------------------------- 1 | module GoGrpcDemo 2 | 3 | go 1.23.2 4 | 5 | require ( 6 | github.com/google/uuid v1.6.0 7 | google.golang.org/grpc v1.67.1 8 | google.golang.org/protobuf v1.35.1 9 | ) 10 | 11 | require ( 12 | golang.org/x/net v0.28.0 // indirect 13 | golang.org/x/sys v0.24.0 // indirect 14 | golang.org/x/text v0.17.0 // indirect 15 | google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect 16 | ) 17 | -------------------------------------------------------------------------------- /9-learn/go.sum: -------------------------------------------------------------------------------- 1 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 2 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 3 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 4 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 5 | golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= 6 | golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= 7 | golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= 8 | golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 9 | golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= 10 | golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= 11 | google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 h1:e7S5W7MGGLaSu8j3YjdezkZ+m1/Nm0uRVRMEMGk26Xs= 12 | google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= 13 | google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= 14 | google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= 15 | google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= 16 | google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= 17 | -------------------------------------------------------------------------------- /9-learn/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "GoGrpcDemo/pb" 5 | "context" 6 | "github.com/google/uuid" 7 | "google.golang.org/grpc" 8 | "log" 9 | "net" 10 | ) 11 | 12 | type userServerImpl struct { 13 | pb.UnimplementedUserServerServer 14 | } 15 | 16 | func (*userServerImpl) Login(ctx context.Context, request *pb.LoginRequest) (*pb.LoginResponse, error) { 17 | log.Printf("Received request: %v", request) 18 | res := &pb.LoginResponse{ 19 | SessionID: uuid.NewString(), 20 | AccessToken: uuid.NewString(), 21 | RefreshToken: uuid.NewString(), 22 | } 23 | return res, nil 24 | } 25 | 26 | func main() { 27 | listen, err := net.Listen("tcp", ":9092") 28 | if err != nil { 29 | log.Panic(err) 30 | } 31 | s := grpc.NewServer() 32 | pb.RegisterUserServerServer(s, &userServerImpl{}) 33 | defer func() { 34 | s.Stop() 35 | err := listen.Close() 36 | if err != nil { 37 | log.Panic(err) 38 | return 39 | } 40 | }() 41 | 42 | log.Printf("Listening on port 9092 \n") 43 | err = s.Serve(listen) 44 | if err != nil { 45 | log.Panic(err) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /9-learn/pb/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afterloe/golangStudy/5e895761ed4738304ddfaf930221df18ca580581/9-learn/pb/.gitkeep -------------------------------------------------------------------------------- /9-learn/pb/user_service.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // versions: 3 | // protoc-gen-go v1.28.1 4 | // protoc v5.28.3 5 | // source: user_service.proto 6 | 7 | package pb 8 | 9 | import ( 10 | protoreflect "google.golang.org/protobuf/reflect/protoreflect" 11 | protoimpl "google.golang.org/protobuf/runtime/protoimpl" 12 | reflect "reflect" 13 | sync "sync" 14 | ) 15 | 16 | const ( 17 | // Verify that this generated code is sufficiently up-to-date. 18 | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) 19 | // Verify that runtime/protoimpl is sufficiently up-to-date. 20 | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) 21 | ) 22 | 23 | type LoginRequest struct { 24 | state protoimpl.MessageState 25 | sizeCache protoimpl.SizeCache 26 | unknownFields protoimpl.UnknownFields 27 | 28 | LoginName string `protobuf:"bytes,1,opt,name=LoginName,proto3" json:"LoginName,omitempty"` 29 | Scrip string `protobuf:"bytes,2,opt,name=Scrip,proto3" json:"Scrip,omitempty"` 30 | } 31 | 32 | func (x *LoginRequest) Reset() { 33 | *x = LoginRequest{} 34 | if protoimpl.UnsafeEnabled { 35 | mi := &file_user_service_proto_msgTypes[0] 36 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 37 | ms.StoreMessageInfo(mi) 38 | } 39 | } 40 | 41 | func (x *LoginRequest) String() string { 42 | return protoimpl.X.MessageStringOf(x) 43 | } 44 | 45 | func (*LoginRequest) ProtoMessage() {} 46 | 47 | func (x *LoginRequest) ProtoReflect() protoreflect.Message { 48 | mi := &file_user_service_proto_msgTypes[0] 49 | if protoimpl.UnsafeEnabled && x != nil { 50 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 51 | if ms.LoadMessageInfo() == nil { 52 | ms.StoreMessageInfo(mi) 53 | } 54 | return ms 55 | } 56 | return mi.MessageOf(x) 57 | } 58 | 59 | // Deprecated: Use LoginRequest.ProtoReflect.Descriptor instead. 60 | func (*LoginRequest) Descriptor() ([]byte, []int) { 61 | return file_user_service_proto_rawDescGZIP(), []int{0} 62 | } 63 | 64 | func (x *LoginRequest) GetLoginName() string { 65 | if x != nil { 66 | return x.LoginName 67 | } 68 | return "" 69 | } 70 | 71 | func (x *LoginRequest) GetScrip() string { 72 | if x != nil { 73 | return x.Scrip 74 | } 75 | return "" 76 | } 77 | 78 | type LoginResponse struct { 79 | state protoimpl.MessageState 80 | sizeCache protoimpl.SizeCache 81 | unknownFields protoimpl.UnknownFields 82 | 83 | SessionID string `protobuf:"bytes,1,opt,name=SessionID,proto3" json:"SessionID,omitempty"` 84 | AccessToken string `protobuf:"bytes,2,opt,name=AccessToken,proto3" json:"AccessToken,omitempty"` 85 | RefreshToken string `protobuf:"bytes,3,opt,name=RefreshToken,proto3" json:"RefreshToken,omitempty"` 86 | } 87 | 88 | func (x *LoginResponse) Reset() { 89 | *x = LoginResponse{} 90 | if protoimpl.UnsafeEnabled { 91 | mi := &file_user_service_proto_msgTypes[1] 92 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 93 | ms.StoreMessageInfo(mi) 94 | } 95 | } 96 | 97 | func (x *LoginResponse) String() string { 98 | return protoimpl.X.MessageStringOf(x) 99 | } 100 | 101 | func (*LoginResponse) ProtoMessage() {} 102 | 103 | func (x *LoginResponse) ProtoReflect() protoreflect.Message { 104 | mi := &file_user_service_proto_msgTypes[1] 105 | if protoimpl.UnsafeEnabled && x != nil { 106 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 107 | if ms.LoadMessageInfo() == nil { 108 | ms.StoreMessageInfo(mi) 109 | } 110 | return ms 111 | } 112 | return mi.MessageOf(x) 113 | } 114 | 115 | // Deprecated: Use LoginResponse.ProtoReflect.Descriptor instead. 116 | func (*LoginResponse) Descriptor() ([]byte, []int) { 117 | return file_user_service_proto_rawDescGZIP(), []int{1} 118 | } 119 | 120 | func (x *LoginResponse) GetSessionID() string { 121 | if x != nil { 122 | return x.SessionID 123 | } 124 | return "" 125 | } 126 | 127 | func (x *LoginResponse) GetAccessToken() string { 128 | if x != nil { 129 | return x.AccessToken 130 | } 131 | return "" 132 | } 133 | 134 | func (x *LoginResponse) GetRefreshToken() string { 135 | if x != nil { 136 | return x.RefreshToken 137 | } 138 | return "" 139 | } 140 | 141 | var File_user_service_proto protoreflect.FileDescriptor 142 | 143 | var file_user_service_proto_rawDesc = []byte{ 144 | 0x0a, 0x12, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 145 | 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x4c, 0x6f, 0x67, 0x69, 0x63, 0x22, 0x42, 0x0a, 0x0c, 0x4c, 146 | 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x4c, 147 | 0x6f, 0x67, 0x69, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 148 | 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x53, 0x63, 0x72, 149 | 0x69, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x53, 0x63, 0x72, 0x69, 0x70, 0x22, 150 | 0x73, 0x0a, 0x0d, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 151 | 0x12, 0x1c, 0x0a, 0x09, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x01, 0x20, 152 | 0x01, 0x28, 0x09, 0x52, 0x09, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x20, 153 | 0x0a, 0x0b, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 154 | 0x01, 0x28, 0x09, 0x52, 0x0b, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 155 | 0x12, 0x22, 0x0a, 0x0c, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 156 | 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 157 | 0x6f, 0x6b, 0x65, 0x6e, 0x32, 0x40, 0x0a, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 158 | 0x65, 0x72, 0x12, 0x32, 0x0a, 0x05, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x13, 0x2e, 0x4c, 0x6f, 159 | 0x67, 0x69, 0x63, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 160 | 0x1a, 0x14, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x63, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 161 | 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x07, 0x5a, 0x05, 0x2e, 0x2f, 0x3b, 0x70, 0x62, 0x62, 162 | 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 163 | } 164 | 165 | var ( 166 | file_user_service_proto_rawDescOnce sync.Once 167 | file_user_service_proto_rawDescData = file_user_service_proto_rawDesc 168 | ) 169 | 170 | func file_user_service_proto_rawDescGZIP() []byte { 171 | file_user_service_proto_rawDescOnce.Do(func() { 172 | file_user_service_proto_rawDescData = protoimpl.X.CompressGZIP(file_user_service_proto_rawDescData) 173 | }) 174 | return file_user_service_proto_rawDescData 175 | } 176 | 177 | var file_user_service_proto_msgTypes = make([]protoimpl.MessageInfo, 2) 178 | var file_user_service_proto_goTypes = []interface{}{ 179 | (*LoginRequest)(nil), // 0: Logic.LoginRequest 180 | (*LoginResponse)(nil), // 1: Logic.LoginResponse 181 | } 182 | var file_user_service_proto_depIdxs = []int32{ 183 | 0, // 0: Logic.UserServer.Login:input_type -> Logic.LoginRequest 184 | 1, // 1: Logic.UserServer.Login:output_type -> Logic.LoginResponse 185 | 1, // [1:2] is the sub-list for method output_type 186 | 0, // [0:1] is the sub-list for method input_type 187 | 0, // [0:0] is the sub-list for extension type_name 188 | 0, // [0:0] is the sub-list for extension extendee 189 | 0, // [0:0] is the sub-list for field type_name 190 | } 191 | 192 | func init() { file_user_service_proto_init() } 193 | func file_user_service_proto_init() { 194 | if File_user_service_proto != nil { 195 | return 196 | } 197 | if !protoimpl.UnsafeEnabled { 198 | file_user_service_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { 199 | switch v := v.(*LoginRequest); i { 200 | case 0: 201 | return &v.state 202 | case 1: 203 | return &v.sizeCache 204 | case 2: 205 | return &v.unknownFields 206 | default: 207 | return nil 208 | } 209 | } 210 | file_user_service_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { 211 | switch v := v.(*LoginResponse); i { 212 | case 0: 213 | return &v.state 214 | case 1: 215 | return &v.sizeCache 216 | case 2: 217 | return &v.unknownFields 218 | default: 219 | return nil 220 | } 221 | } 222 | } 223 | type x struct{} 224 | out := protoimpl.TypeBuilder{ 225 | File: protoimpl.DescBuilder{ 226 | GoPackagePath: reflect.TypeOf(x{}).PkgPath(), 227 | RawDescriptor: file_user_service_proto_rawDesc, 228 | NumEnums: 0, 229 | NumMessages: 2, 230 | NumExtensions: 0, 231 | NumServices: 1, 232 | }, 233 | GoTypes: file_user_service_proto_goTypes, 234 | DependencyIndexes: file_user_service_proto_depIdxs, 235 | MessageInfos: file_user_service_proto_msgTypes, 236 | }.Build() 237 | File_user_service_proto = out.File 238 | file_user_service_proto_rawDesc = nil 239 | file_user_service_proto_goTypes = nil 240 | file_user_service_proto_depIdxs = nil 241 | } 242 | -------------------------------------------------------------------------------- /9-learn/pb/user_service_grpc.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go-grpc. DO NOT EDIT. 2 | // versions: 3 | // - protoc-gen-go-grpc v1.2.0 4 | // - protoc v5.28.3 5 | // source: user_service.proto 6 | 7 | package pb 8 | 9 | import ( 10 | context "context" 11 | grpc "google.golang.org/grpc" 12 | codes "google.golang.org/grpc/codes" 13 | status "google.golang.org/grpc/status" 14 | ) 15 | 16 | // This is a compile-time assertion to ensure that this generated file 17 | // is compatible with the grpc package it is being compiled against. 18 | // Requires gRPC-Go v1.32.0 or later. 19 | const _ = grpc.SupportPackageIsVersion7 20 | 21 | // UserServerClient is the client API for UserServer service. 22 | // 23 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. 24 | type UserServerClient interface { 25 | Login(ctx context.Context, in *LoginRequest, opts ...grpc.CallOption) (*LoginResponse, error) 26 | } 27 | 28 | type userServerClient struct { 29 | cc grpc.ClientConnInterface 30 | } 31 | 32 | func NewUserServerClient(cc grpc.ClientConnInterface) UserServerClient { 33 | return &userServerClient{cc} 34 | } 35 | 36 | func (c *userServerClient) Login(ctx context.Context, in *LoginRequest, opts ...grpc.CallOption) (*LoginResponse, error) { 37 | out := new(LoginResponse) 38 | err := c.cc.Invoke(ctx, "/Logic.UserServer/Login", in, out, opts...) 39 | if err != nil { 40 | return nil, err 41 | } 42 | return out, nil 43 | } 44 | 45 | // UserServerServer is the server API for UserServer service. 46 | // All implementations must embed UnimplementedUserServerServer 47 | // for forward compatibility 48 | type UserServerServer interface { 49 | Login(context.Context, *LoginRequest) (*LoginResponse, error) 50 | mustEmbedUnimplementedUserServerServer() 51 | } 52 | 53 | // UnimplementedUserServerServer must be embedded to have forward compatible implementations. 54 | type UnimplementedUserServerServer struct { 55 | } 56 | 57 | func (UnimplementedUserServerServer) Login(context.Context, *LoginRequest) (*LoginResponse, error) { 58 | return nil, status.Errorf(codes.Unimplemented, "method Login not implemented") 59 | } 60 | func (UnimplementedUserServerServer) mustEmbedUnimplementedUserServerServer() {} 61 | 62 | // UnsafeUserServerServer may be embedded to opt out of forward compatibility for this service. 63 | // Use of this interface is not recommended, as added methods to UserServerServer will 64 | // result in compilation errors. 65 | type UnsafeUserServerServer interface { 66 | mustEmbedUnimplementedUserServerServer() 67 | } 68 | 69 | func RegisterUserServerServer(s grpc.ServiceRegistrar, srv UserServerServer) { 70 | s.RegisterService(&UserServer_ServiceDesc, srv) 71 | } 72 | 73 | func _UserServer_Login_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 74 | in := new(LoginRequest) 75 | if err := dec(in); err != nil { 76 | return nil, err 77 | } 78 | if interceptor == nil { 79 | return srv.(UserServerServer).Login(ctx, in) 80 | } 81 | info := &grpc.UnaryServerInfo{ 82 | Server: srv, 83 | FullMethod: "/Logic.UserServer/Login", 84 | } 85 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 86 | return srv.(UserServerServer).Login(ctx, req.(*LoginRequest)) 87 | } 88 | return interceptor(ctx, in, info, handler) 89 | } 90 | 91 | // UserServer_ServiceDesc is the grpc.ServiceDesc for UserServer service. 92 | // It's only intended for direct use with grpc.RegisterService, 93 | // and not to be introspected or modified (even as a copy) 94 | var UserServer_ServiceDesc = grpc.ServiceDesc{ 95 | ServiceName: "Logic.UserServer", 96 | HandlerType: (*UserServerServer)(nil), 97 | Methods: []grpc.MethodDesc{ 98 | { 99 | MethodName: "Login", 100 | Handler: _UserServer_Login_Handler, 101 | }, 102 | }, 103 | Streams: []grpc.StreamDesc{}, 104 | Metadata: "user_service.proto", 105 | } 106 | -------------------------------------------------------------------------------- /9-learn/proto/user_service.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "./;pb"; 4 | 5 | package Logic; 6 | 7 | service UserServer { 8 | rpc Login(LoginRequest) returns (LoginResponse); 9 | } 10 | 11 | message LoginRequest { 12 | string LoginName = 1; 13 | string Scrip = 2; 14 | } 15 | 16 | message LoginResponse { 17 | string SessionID = 1; 18 | string AccessToken = 2; 19 | string RefreshToken =3; 20 | } 21 | -------------------------------------------------------------------------------- /9-learn/summary.md: -------------------------------------------------------------------------------- 1 | 第九天 2 | === 3 | golang 构建rpc服务, 过程可参考[文档](https://grpc.org.cn/docs/languages/go/) 4 | 5 | ## 构建基础环境 6 | ### 安装protoc 7 | ```shell 8 | apt install -y protoc 9 | 10 | protoc --version 11 | libprotoc 25.1 12 | ``` 13 | 或者从github官网下载 14 | ```shell 15 | wget https://objects.githubusercontent.com/github-production-release-asset-2e65be/23357588/492b7b27-88e2-457a-8613-05c78a4010b5?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=releaseassetproduction%2F20241104%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20241104T021518Z&X-Amz-Expires=300&X-Amz-Signature=f2ae84ea7d203d191167443c615868f38df4842f926d4959f21977e9a2469f2b&X-Amz-SignedHeaders=host&response-content-disposition=attachment%3B%20filename%3Dprotoc-29.0-rc-2-linux-x86_64.zip&response-content-type=application%2Foctet-stream 16 | ls 17 | protoc-28.3-linux-x86_64.zip 18 | 19 | cd /usr/local/src 20 | mkdir protoc-bin 21 | cd protoc-bin 22 | mv ~/Downloads/protoc-28.3-linux-x86_64.zip . 23 | unzip protoc-28.3-linux-x86_64.zip 24 | ls 25 | bin include protoc-28.3-linux-x86_64.zip readme.txt 26 | cd /usr/local/bin 27 | ln -s /usr/local/protoc-bin/bin/protoc . 28 | protoc --version 29 | libprotoc 25.1 30 | ``` 31 | 32 | ### 编写proto文件 33 | ```protobuf 34 | syntax = "proto3"; 35 | 36 | option go_package = "./;pb"; 37 | 38 | package Logic; 39 | 40 | service UserServer { 41 | rpc Login(LoginRequest) returns (LoginResponse); 42 | } 43 | 44 | message LoginRequest { 45 | string LoginName = 1; 46 | string Scrip = 2; 47 | } 48 | 49 | message LoginResponse { 50 | string SessionID = 1; 51 | string AccessToken = 2; 52 | string RefreshToken =3; 53 | } 54 | ``` 55 | * syntax: 制定protobuf版本 56 | * option go_package 表示生成的代码存放位置 57 | > 具体编写内容可参考[这里](https://developers.google.com/protocol-buffers) 58 | 59 | ### 安装go的proto插件 60 | ```shell 61 | sudo apt install systemd-dev # 安装插件时可能会出一些莫名其妙的bug,如果失败了 就现安装这个 62 | go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28 63 | go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2 64 | 65 | # 添加到path中,便于proto查询插件 66 | export PATH="$PATH:$(go env GOPATH)/bin" 67 | ``` 68 | 69 | ### 使用插件生成pb文件 70 | ```shell 71 | protoc --proto_path=proto --go_out=pb --go_opt=paths=source_relative \ 72 | --go-grpc_out=pb --go-grpc_opt=paths=source_relative \ 73 | proto/*.proto 74 | ``` 75 | * proto_path proto文件存放的目录 76 | * go_out 输出文件存放位置 77 | 78 | ```shell 79 | ls pb 80 | user_service_grpc.pb.go user_service.pb.go 81 | ``` 82 | 执行成功后会有两个文件 83 | 84 | ## 开发gRPC服务 85 | ### 实现proto文件中定义的服务 86 | 87 | ```go 88 | package main 89 | 90 | import ( 91 | "GoGrpcDemo/pb" // proto 文件生成pb文件的目录 引用进来 92 | "errors" 93 | ) 94 | 95 | type userServerImpl struct { 96 | pb.UnimplementedUserServerServer 97 | } 98 | 99 | // Login 实现proto文件中定义的方法 100 | func (*userServerImpl) Login(...interface{}) (interface{}, error) { 101 | // TODO 102 | return nil, errors.New("not impl") 103 | } 104 | ``` 105 | 编写gRPC服务 106 | ```go 107 | package main 108 | 109 | import ( 110 | "log" 111 | "net" 112 | "google.golang.org/grpc" 113 | "GoGrpcDemo/pb" 114 | "GoGrpcDemo/logic" 115 | ) 116 | 117 | func main() { 118 | listen, err := net.Listen("tcp", ":9092") // 监听地址 119 | if err != nil { 120 | log.Panic(err) 121 | } 122 | s := grpc.NewServer() // 获取grpc服务实例 123 | pb.RegisterUserServerServer(s, &userServerImpl{}) // 注册proto服务 124 | 125 | // 启动服务后需要关闭 126 | defer func() { 127 | s.Stop() 128 | err := listen.Close() 129 | if err != nil { 130 | log.Panic(err) 131 | return 132 | } 133 | }() 134 | 135 | log.Printf("Listening on port 9092 \n") 136 | err = s.Serve(listen) // 启动监听 137 | if err != nil { 138 | log.Panic(err) 139 | } 140 | } 141 | ``` 142 | 143 | ## 构建及测试 144 | ### 启动gRPC服务 145 | ```shell 146 | go build -v . 147 | 148 | ./GoGrpcDemo 149 | 2024/11/05 11:41:36 Listening on port 9092 150 | ``` 151 | 152 | ### 启动C++ gRPC客户端 153 | ```shell 154 | ./CppGrpcClient 155 | Begin to readlocalhost:9092 156 | Login success: 177150ca-d397-4279-a091-61c8ba16a904 a32a785e-fbbe-4a78-ab6b-9d5589f20882 157 | ``` 158 | 159 | ### gRPC日志 160 | ```shell 161 | 2024/11/05 11:41:36 Listening on port 9092 162 | 2024/11/05 11:43:06 Received request: LoginName:"afterloe" Scrip:"111111" 163 | ``` -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at 605728727@qq.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 afterloe.L 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go语言学习笔记 2 | 3 | > create by [afterloe](lm6289511@gmail.com) 4 | > version is 1.4 5 | > MIT License 6 | 7 | ## 目录 8 | - 笔记 9 | - 备忘录 10 | - golang 环境准备 11 | 12 | ## golang 教程 学习笔记&开发日记 13 | golang 开发的规范请参考[官方的标准](https://github.com/golang/go/wiki/CodeReviewComments) 14 | 15 | ### 笔记 16 | golang day08中的项目已经单独立项了,可以关注 https://github.com/afterloe/AwPaas 下的 https://github.com/afterloe/awpaas-manager 项目 17 | > 时隔半年继续更新,用go做了一个项目,将项目中碰到的点滴分享一下。将原来的第七天的内容改为了udp,第九天增加了c++扩展, 具体目录如下 18 | 19 | 直接阅读点[这里](./SUMMARY.md) 20 | 21 | * [第一天](1-learn/summary.md) 22 | * 变量、常量 23 | * map & slice 的初始化及使用 24 | * make & new 以及默认值 25 | * [第二天](2-learn/summary.md) 26 | * 函数、流程、goto、循环 27 | * [结构体 struct](2-learn/struct.md) 28 | * [第三天](3-learn/summary.md) 29 | * channel、多线程 30 | * 反射、匿名函数、interface继承 31 | * [第四天](4-learn/summary.md) 32 | * 反射的使用 33 | * 读取、写入文件与json处理 34 | * 分包分模块使用 35 | * [第五天](5-learn/summary.md) 36 | * 数据库连接 37 | * 字符串模板使用,输出流字符串转换 38 | * interface进阶使用 39 | * [第六天](6-learn/summary.md) 40 | * http服务之默认路由 41 | * 自定义路由 42 | * 文件上传 43 | * [第七天](7-learn/index.md) 44 | * udp服务 45 | * udp客户端 46 | * 远程命令控制 47 | * [第八天](8-learn/summary.md) 48 | * 单元测试 49 | * 压力、性能测试 50 | * mock 测试 51 | * [第九天](9-learn/summary.md) 52 | * 编译安装protoc 53 | * [gRPC服务构建步骤](9-learn/main.go) 54 | * [C++客户端调用测试](9-learn/build_cpp_grpc_client.md) 55 | * [第十天](10-learn/README.md) 56 | * [增强Context, gin封装web服务](10-learn/enhance_context.md) 57 | * [国密算法集成](10-learn/use_gmssl.md) 58 | * [jwt Token](10-learn/bear_token.md) 59 | * [Swagger 配置](10-learn/use_swagger.md) 60 | * [构建通用Go打包镜像](10-learn/build_by_docker.md) 61 | 62 | ## 备忘录 63 | 64 | ## golang 环境准备 65 | ```bash 66 | wget https://dl.google.com/go/go1.12.linux-amd64.tar.gz 67 | sudo tar xzvf go1.12.linux-amd64.tar.gz -C /usr/local/ 68 | ``` 69 | 70 | 配置go path 及 go root 71 | ```bash 72 | $ sudo vim /etc/profile.d/go.sh 73 | export GOROOT=/usr/local/go 74 | export PATH=$PATH:$GOROOT/bin:$GOPATH/bin 75 | 76 | # Set the GOPROXY environment variable 77 | export GOPROXY=https://goproxy.io,direct 78 | # Set environment variable allow bypassing the proxy for specified repos (optional) 79 | export GOPRIVATE=git.mycompany.com,github.com/my/private 80 | ``` 81 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # 目录 2 | 3 | * [前言](README.md) 4 | * [第一天](1-learn/summary.md) 5 | * 变量、常量 6 | * map & slice 的初始化及使用 7 | * make & new 以及默认值 8 | * [第二天](2-learn/summary.md) 9 | * 函数、流程、goto、循环 10 | * [结构体 struct](2-learn/struct.md) 11 | * [第三天](3-learn/summary.md) 12 | * channel、多线程 13 | * 反射、匿名函数、interface继承 14 | * [第四天](4-learn/summary.md) 15 | * 反射的使用 16 | * 读取、写入文件与json处理 17 | * 分包分模块使用 18 | * [第五天](5-learn/summary.md) 19 | * 数据库连接 20 | * 字符串模板使用,输出流字符串转换 21 | * interface进阶使用 22 | * [第六天](6-learn/summary.md) 23 | * http服务之默认路由 24 | * 自定义路由 25 | * 文件上传 26 | * [第七天](7-learn/index.md) 27 | * udp服务 28 | * udp客户端 29 | * 远程命令控制 30 | * [第八天](8-learn/summary.md) 31 | * 单元测试 32 | * 压力、性能测试 33 | * mock 测试 34 | * [第九天](9-learn/summary.md) 35 | * 编译安装protoc 36 | * [gRPC服务构建步骤](9-learn/main.go) 37 | * [C++客户端调用测试](9-learn/build_cpp_grpc_client.md) 38 | * [第十天](10-learn/README.md) 39 | * [增强Context, gin封装web服务](10-learn/enhance_context.md) 40 | * [国密算法集成](10-learn/use_gmssl.md) 41 | * [jwt Token](10-learn/bear_token.md) 42 | * [Swagger 配置](10-learn/use_swagger.md) 43 | * [构建通用Go打包镜像](10-learn/build_by_docker.md) -------------------------------------------------------------------------------- /officialGuide/1-tour.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | func main(){ 10 | rand.Seed(time.Now().Unix()) // 类似c/c++里面的math函数一样,需要设置time才能随机 11 | randNum := rand.Intn(10) 12 | fmt.Printf("My Rand number is %d \r\n", randNum) 13 | } 14 | -------------------------------------------------------------------------------- /officialGuide/10-tour.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type I interface { 6 | M() 7 | } 8 | 9 | type T struct { 10 | S string 11 | } 12 | 13 | // 此方法表示类型 T 实现了接口 I,但我们无需显式声明此事。 14 | func (t T) M() { 15 | fmt.Println(t.S) 16 | } 17 | 18 | func main() { 19 | var i I = T{"hello"} 20 | i.M() 21 | } 22 | -------------------------------------------------------------------------------- /officialGuide/11-tour.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | ) 7 | 8 | type I interface { 9 | M() 10 | } 11 | 12 | type T struct { 13 | S string 14 | } 15 | 16 | func (t *T) M() { 17 | fmt.Println(t.S) 18 | } 19 | 20 | type F float64 21 | 22 | func (f F) M() { 23 | fmt.Println(f) 24 | } 25 | 26 | func main(){ 27 | var i I 28 | i = &T {"Afterloe"} 29 | describe(i) 30 | i.M() 31 | 32 | i = F(math.Pi) 33 | describe(i) 34 | i.M() 35 | } 36 | 37 | func describe(i I) { 38 | fmt.Printf("(%v, %T) \r\n", i, i) 39 | } 40 | -------------------------------------------------------------------------------- /officialGuide/12-tour.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main(){ 6 | var i interface{} = "afterloe" 7 | 8 | s := i.(string) 9 | fmt.Println(s) 10 | 11 | // 推荐的 interface 类型转换 12 | s, ok := i.(string) 13 | fmt.Println(s, ok) 14 | 15 | f, ok := i.(float64) 16 | fmt.Println(f, ok) 17 | 18 | // 会报错 panic 19 | f = i.(float64) 20 | fmt.Println(f) 21 | } 22 | 23 | -------------------------------------------------------------------------------- /officialGuide/13-tour.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func do(i interface{}) { 6 | switch v := i.(type) { 7 | case int: 8 | fmt.Printf("this is int, Twice %v is %v \r\n", v, v*2) 9 | case string: 10 | fmt.Printf("this is string, %q is %v bytes long \r\n", v, len(v)) 11 | default: 12 | fmt.Printf("I don't know about this type %T! \r\n", v) 13 | } 14 | } 15 | 16 | func main(){ 17 | do(21) 18 | do("hello") 19 | do(true) 20 | } 21 | 22 | -------------------------------------------------------------------------------- /officialGuide/14-tour.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type IPAddr [4]byte 6 | 7 | func (i *IPAddr) String() string { 8 | return fmt.Sprintf("%v.%v.%v.%v", i[0], i[1], i[2], i[3]) 9 | } 10 | 11 | func main(){ 12 | 13 | /** 14 | 15 | hosts := map[string]IPAddr { 16 | "loopback": {127, 0, 0, 1}, 17 | "fuzhouDNS": {218, 85, 157, 99}, 18 | "googleDNS": {8, 8, 8, 8}, 19 | "telecomDNS": {114, 114, 114, 114}, 20 | } 21 | */ 22 | 23 | var hosts = make(map[string]*IPAddr) 24 | hosts["loopback"] = &IPAddr{127, 0, 0, 1} 25 | hosts["fuzhouDNS"] = &IPAddr{218, 85, 157, 99} 26 | hosts["googleDNS"] = &IPAddr{8, 8, 8, 8} 27 | hosts["telecomDNS"] = &IPAddr{114, 114, 114, 114} 28 | 29 | for name, ptr := range hosts { 30 | fmt.Printf("%v: %v \r\n", name, ptr) 31 | } 32 | } 33 | 34 | -------------------------------------------------------------------------------- /officialGuide/15-tour.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | "strconv" 7 | ) 8 | 9 | type MyError struct { 10 | When time.Time 11 | What string 12 | } 13 | 14 | func (e *MyError) Error() string { 15 | return fmt.Sprintf("at %v, %s", e.When, e.What) 16 | } 17 | 18 | func run() error { 19 | return &MyError { 20 | time.Now(), 21 | "it didn't work", 22 | } 23 | } 24 | 25 | func main(){ 26 | if err := run(); err != nil { 27 | // panic(err) 28 | fmt.Println(err) 29 | } 30 | 31 | i, err := strconv.Atoi("122") 32 | if nil != err { 33 | fmt.Printf("Couldn't convert numnber: %v \r\n", err) 34 | } 35 | fmt.Printf("Converted integer: %d \r\n", i) 36 | } 37 | 38 | -------------------------------------------------------------------------------- /officialGuide/16-tour.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "strings" 7 | ) 8 | 9 | func main(){ 10 | 11 | r := strings.NewReader("Hello, Reader! i'm afterloe") 12 | b := make([]byte, 8) 13 | for { 14 | n, err := r.Read(b) 15 | fmt.Printf("n= %v, err= %v b= %v \r\n", n, err, b) 16 | fmt.Printf("b[:n] = %q \r\n", b[:n]) 17 | if io.EOF == err { 18 | break 19 | } 20 | } 21 | } 22 | 23 | -------------------------------------------------------------------------------- /officialGuide/17-tour.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "golang.org/x/tour/reader" 4 | 5 | type MyReader struct {} 6 | 7 | func (r MyReader) Read(buf []byte) (int, error) { 8 | buf[0] = 'A' 9 | return 1, nil 10 | } 11 | 12 | func main() { 13 | reader.Validate(MyReader{}) 14 | } 15 | -------------------------------------------------------------------------------- /officialGuide/18-tour.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io" 5 | "os" 6 | "strings" 7 | ) 8 | 9 | type rot13Reader struct { 10 | r io.Reader 11 | } 12 | 13 | func rot13(b byte) byte { 14 | switch { 15 | case 'A' <= b && b <='M': 16 | b = b + 13 17 | case 'M' < b && b <= 'Z': 18 | b = b - 13 19 | case 'a' <= b && b <= 'm': 20 | b = b + 13 21 | case 'm' < b && b <= 'z': 22 | b = b - 13 23 | } 24 | return b; 25 | } 26 | 27 | func (r rot13Reader) Read (buf []byte) (int, error) { 28 | n, e := r.r.Read(buf) 29 | for i := 0; i < n; i++ { 30 | buf[i] = rot13(buf[i]) 31 | } 32 | return n, e 33 | } 34 | 35 | func main(){ 36 | s := strings.NewReader("Lbh penpxrq gur pbqr!") 37 | r := rot13Reader{s} 38 | io.Copy(os.Stdout, &r) 39 | } 40 | 41 | -------------------------------------------------------------------------------- /officialGuide/19-tour.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "image" 6 | ) 7 | 8 | func main() { 9 | m := image.NewRGBA(image.Rect(0, 0, 100, 100)) 10 | fmt.Println(m.Bounds()) 11 | fmt.Println(m.At(0, 0).RGBA()) 12 | } 13 | 14 | -------------------------------------------------------------------------------- /officialGuide/2-tour.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func add(x, y, sum *int) { 6 | fmt.Printf("function add x -> %d \r\n", *x) 7 | fmt.Printf("function add y -> %d \r\n", *y) 8 | *sum = *x + *y 9 | } 10 | 11 | func main() { 12 | var ( 13 | x = 10 14 | y = 22 15 | ) 16 | sum := new(int) 17 | add(&x, &y, sum) 18 | fmt.Printf("10 + 22 = %d \r\n", *sum) 19 | } 20 | -------------------------------------------------------------------------------- /officialGuide/20-tour.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "golang.org/x/tour/pic" 5 | "image/color" 6 | "image" 7 | ) 8 | 9 | type Image struct{} 10 | 11 | func (i Image) ColorModel() color.Model { 12 | return color.RGBAModel 13 | } 14 | 15 | func (i Image) Bounds() image.Rectangle { 16 | return image.Rect(0,0,200,200) 17 | } 18 | 19 | func (i Image) At(x, y int) color.Color { 20 | return color.RGBA{uint8(x), uint8(y), uint8(255), uint8(255)} 21 | } 22 | 23 | func main(){ 24 | m := Image{} 25 | pic.ShowImage(m) 26 | } 27 | 28 | -------------------------------------------------------------------------------- /officialGuide/21-tour.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func say(s string) { 9 | for i := 0; i < 5; i++ { 10 | time.Sleep(100 * time.Millisecond) 11 | fmt.Println(s) 12 | } 13 | } 14 | 15 | func main(){ 16 | go say("afterloe") 17 | say("hello") 18 | } 19 | 20 | -------------------------------------------------------------------------------- /officialGuide/22-tour.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func sum(s []int, c chan int) { 6 | sum := 0 7 | for _,v := range s { 8 | sum += v 9 | } 10 | c <-sum 11 | } 12 | 13 | func main(){ 14 | s := []int{7, 2, 8, -9, 4, 2} 15 | 16 | c := make(chan int) 17 | go sum(s[:len(s)/2], c) 18 | go sum(s[len(s)/2:], c) 19 | x, y := <-c, <-c 20 | 21 | fmt.Printf("%d\t%d\t%d\r\n", x, y, x+y) 22 | } 23 | 24 | -------------------------------------------------------------------------------- /officialGuide/23-tour.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main(){ 6 | /** 7 | 8 | 仅当信道的缓冲区填满后,向其发送数据时才会阻塞。当缓冲区为空时,接受方会阻塞。 9 | 10 | */ 11 | ch := make(chan int, 2) 12 | ch <- 1 13 | ch <- 2 14 | fmt.Println(<-ch) 15 | fmt.Println(<-ch) 16 | } 17 | 18 | -------------------------------------------------------------------------------- /officialGuide/24-tour.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func fibonacci(c, quit chan int) { 6 | x, y := 0, 1 7 | for { 8 | select { 9 | case c <- x: 10 | x, y = y, x+y 11 | case <-quit: 12 | fmt.Println("quit") 13 | return 14 | 15 | } 16 | } 17 | } 18 | 19 | func main() { 20 | c := make(chan int) 21 | quit := make(chan int) 22 | go func() { 23 | for i := 0; i < 30; i++ { 24 | fmt.Println(<-c) 25 | } 26 | quit <- 0 27 | }() 28 | fibonacci(c, quit) 29 | } 30 | -------------------------------------------------------------------------------- /officialGuide/25-tour.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func main(){ 9 | 10 | tick := time.Tick(100 * time.Millisecond) 11 | boom := time.After(500 * time.Millisecond) 12 | for { 13 | select { 14 | case <-tick: 15 | fmt.Println("tick.") 16 | case <-boom: 17 | fmt.Println("BOOM!") 18 | return 19 | default: 20 | fmt.Println(" .") 21 | time.Sleep(50 * time.Millisecond) 22 | } 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /officialGuide/26-tour.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "golang.org/x/tour/tree" 5 | "fmt" 6 | ) 7 | 8 | func Walk (t *tree.Tree, ch chan int) { 9 | if t.Left != nil { 10 | Walk(t.Left, ch) 11 | } 12 | ch <- t.Value 13 | if t.Right != nil { 14 | Walk(t.Right, ch) 15 | } 16 | } 17 | 18 | func TestWalk() { 19 | ch := make(chan int, 10) 20 | k := 2 21 | go Walk(tree.New(k), ch) 22 | for i := 1; i <= 10; i++ { 23 | val := <-ch 24 | if i * k != val { 25 | fmt.Println("FAIL: Walk() goes wrong.") 26 | return 27 | } 28 | } 29 | fmt.Println("PASS: Walk() goes well") 30 | } 31 | 32 | func Same(t1, t2 *tree.Tree) bool { 33 | ch1 := make(chan int, 10) 34 | ch2 := make(chan int, 10) 35 | go Walk(t1, ch1) 36 | go Walk(t2, ch2) 37 | for i := 0; i < 10; i++ { 38 | n1, n2 := <-ch1, <-ch2 39 | if n1 != n2 { 40 | return false 41 | } 42 | } 43 | return true 44 | } 45 | 46 | func main(){ 47 | ch := make(chan int, 10) 48 | k := 1 49 | go Walk(tree.New(k), ch) 50 | for i := 0; i < 10; i++ { 51 | fmt.Println(<-ch) 52 | } 53 | TestWalk() 54 | 55 | same := Same(tree.New(1), tree.New(1)) 56 | if same { 57 | fmt.Println("PASS : they are same") 58 | } else { 59 | fmt.Println("FAIL : they should be same.") 60 | } 61 | 62 | same = Same(tree.New(1), tree.New(2)) 63 | if !same { 64 | fmt.Println("PASS : they are not same") 65 | } else { 66 | fmt.Println("FAIL : they should be not same") 67 | } 68 | } 69 | 70 | -------------------------------------------------------------------------------- /officialGuide/27-tour.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | // Go协程能够访问一个共享的变量 10 | type SafeCounter struct { 11 | v map[string]int 12 | mux sync.Mutex // sync.Mutex 互斥锁 Lock、Unlock 13 | } 14 | 15 | func (c *SafeCounter) Inc(key string) { 16 | c.mux.Lock() 17 | c.v[key]++ 18 | c.mux.Unlock() 19 | } 20 | 21 | func (c *SafeCounter) Value(key string) int { 22 | c.mux.Lock() 23 | defer c.mux.Unlock() // 解锁 24 | return c.v[key] 25 | } 26 | 27 | func main() { 28 | c := SafeCounter{v: make(map[string]int)} 29 | for i := 0; i < 1000; i++ { 30 | go c.Inc("somekey") 31 | } 32 | 33 | time.Sleep(time.Second) 34 | fmt.Println(c.Value("somekey")) 35 | } 36 | -------------------------------------------------------------------------------- /officialGuide/3-tour.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type Vertex struct { 6 | X, Y int 7 | } 8 | 9 | func main(){ 10 | v := Vertex{1, 2} 11 | ptr := &v 12 | // (*ptr).X = 1e9 13 | ptr.X = 1e9 // 两种方式都可以使用, 这种是语法糖 14 | fmt.Printf("Vertex is %v\r\n", v) 15 | } 16 | 17 | -------------------------------------------------------------------------------- /officialGuide/4-tour.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type Vertex struct { 6 | X,Y int 7 | } 8 | 9 | func main(){ 10 | // 结构体的申明方式 11 | var ( 12 | v1 = Vertex{11, 23} 13 | v2 = Vertex{X: 12} 14 | v3 = Vertex{} 15 | v4 = &Vertex{22, 24} 16 | ) 17 | fmt.Printf("%v\r\n%v\r\n%v\r\n%v\r\n", v1, v2, v3, v4) 18 | } 19 | 20 | -------------------------------------------------------------------------------- /officialGuide/5-tour.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main(){ 6 | 7 | var pow = []int {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 8 | for i, v := range pow { 9 | fmt.Printf("key -> %d, value -> %v \r\n", i, v) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /officialGuide/6-tour.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type Vertex struct { 6 | Lat, Lon float64 7 | 8 | } 9 | 10 | func main(){ 11 | 12 | var m map[string]Vertex 13 | m = make(map[string]Vertex) 14 | m["福州"] = Vertex{26.08, 119.28} 15 | m["泉州"] = Vertex{24.88, 118.67} 16 | m["深圳"] = Vertex{22.32, 114.03} 17 | fmt.Printf("m -> %v \r\n", m) 18 | fmt.Printf("福州 -> %v \r\n", m["福州"]) 19 | 20 | // 第二中写法 21 | var m1 = map[string]Vertex{ 22 | "厦门": Vertex{24.48, 118.08}, 23 | "兰溪": {29.19, 119.48}, // 这样写也是可以的 24 | "漳州": Vertex{23.82, 117.12}, 25 | } 26 | fmt.Printf("m1 -> %v \r\n", m1) 27 | v, ok := m1["厦门"] 28 | fmt.Printf("value -> %v, flag is %v \r\n", v, ok) 29 | delete(m1, "厦门") // 删除key 30 | v, ok = m1["厦门"] 31 | fmt.Printf("value -> %v, flag is %v \r\n", v, ok) 32 | } 33 | 34 | -------------------------------------------------------------------------------- /officialGuide/7-tour.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | ) 7 | 8 | func compute(fn func(float64, float64) float64) float64 { 9 | return fn(3, 4) 10 | } 11 | 12 | func main(){ 13 | 14 | // 函数亦可以当做参数进行传递 15 | hypot := func(x, y float64) float64 { 16 | return math.Sqrt(x*x + y*y) 17 | } 18 | fmt.Printf("value is %f \r\n", hypot(5, 12)) 19 | 20 | fmt.Printf("compute value is %f \r\n", compute(hypot)) 21 | fmt.Printf("compute Pow value is %f \r\n", compute(math.Pow)) 22 | } 23 | 24 | -------------------------------------------------------------------------------- /officialGuide/8-tour.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | ) 7 | 8 | type Vertex struct { 9 | Lat, Lon float64 10 | } 11 | 12 | // 指针操作 13 | func (v *Vertex) Scale(f float64) { 14 | v.Lat = v.Lat * f; 15 | v.Lon = v.Lon * f; 16 | } 17 | 18 | // 结构体的方法 19 | func (v Vertex) Abs() float64 { 20 | return math.Sqrt(v.Lat* v.Lat + v.Lon* v.Lon) 21 | } 22 | 23 | // 使用结构体的方法 24 | func Abs(v *Vertex) float64 { 25 | return math.Sqrt(v.Lat* v.Lat + v.Lon* v.Lon) 26 | } 27 | 28 | func main(){ 29 | v := Vertex{3, 4} 30 | fmt.Printf("string by struct -> %f \r\n", v.Abs()) 31 | fmt.Printf("string by func -> %f \r\n", Abs(&v)) 32 | v.Scale(10) 33 | fmt.Printf("string by struct -> %f \r\n", v.Abs()) 34 | } 35 | 36 | -------------------------------------------------------------------------------- /officialGuide/9-tour.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | ) 7 | 8 | type Abser interface { 9 | Abs() float64 10 | } 11 | 12 | func main() { 13 | var ptr Abser 14 | f := MyFloat(-math.Sqrt2) 15 | v := Vertex{3, 4} 16 | 17 | ptr = f 18 | fmt.Printf("myfloat value is %f \r\n", ptr.Abs()) 19 | // 因为Vertex的指针实现了 Abs的方法,所以直接使用方法是无法调用的。 20 | ptr = &v 21 | fmt.Printf("myfloat value is %f \r\n", ptr.Abs()) 22 | } 23 | 24 | type MyFloat float64 25 | 26 | func (f MyFloat) Abs() float64 { 27 | if 0 > f { 28 | return float64(-f) 29 | } else { 30 | return float64(f) 31 | } 32 | } 33 | 34 | type Vertex struct { 35 | X, Y float64 36 | } 37 | 38 | func (v *Vertex) Abs() float64 { 39 | return math.Sqrt(v.X * v.X + v.Y * v.Y) 40 | } 41 | -------------------------------------------------------------------------------- /officialGuide/summary.md: -------------------------------------------------------------------------------- 1 | # A Tour of Go 2 | > create by (afterloe)[605728727@qq.com] 3 | > MIT License 4 | > version 1.0 5 | 6 | ## Packages 7 | 每个Go语言由(Packages)包构成, 通过 import进行导入, 单行的可以使用`import "fmt"`进行,多个包引入可以使用如下方法 8 | ``` 9 | import ( 10 | "fmt" 11 | "math" 12 | ) 13 | ``` 14 | > 不过使用分组导入语句,效果会更好 15 | 16 | 17 | --------------------------------------------------------------------------------