├── .github └── workflows │ └── ci-cd.yml ├── LICENSE ├── README.md ├── lesson00 ├── images │ ├── 1.png │ ├── image-20231007213757592.png │ ├── image-20231007213835122.png │ ├── image-20231007213951785.png │ ├── image-20231007214308703.png │ ├── image-20231007214410280.png │ ├── image-20231007214554512.png │ ├── image-20231007215945173.png │ ├── image-20231008104402198.png │ ├── image-20231008105637726.png │ ├── image-20231008110110760.png │ └── image-20231008123718393.png └── 从 0 开始学 Go.md ├── lesson01 └── 蓝山工作室——Golang第一节课.md ├── lesson02 ├── 02.md └── images │ ├── img1.png │ ├── img2.png │ ├── img3.png │ └── img4.png ├── lesson03 └── 第三周课件.md ├── lesson04 ├── img │ ├── img01.png │ ├── img02.png │ ├── img03.png │ └── img04.png └── 后端 Go 第四次课.md ├── lesson05 ├── images │ ├── 1.png │ ├── 10.png │ ├── 11.png │ ├── 12.png │ ├── 13.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ ├── 7.png │ ├── 8.png │ └── 9.png └── 并发.md ├── lesson06 ├── README.md ├── clain │ └── main.go ├── m1 │ └── main.go └── m2 │ ├── api │ ├── middleware │ │ ├── cors.go │ │ └── jwt.go │ ├── router.go │ └── user.go │ ├── dao │ └── user.go │ ├── main.go │ ├── model │ └── user.go │ └── utils │ └── response.go ├── lesson07 └── lesson7.md ├── lesson08 ├── README.md └── images │ ├── 1.png │ ├── 2.png │ └── 3.png ├── lesson09 ├── README.md ├── example1 │ ├── go.mod │ ├── go.sum │ └── main.go └── example2 │ ├── baidu │ ├── a2.png │ ├── carm-baidu-black.png │ ├── carm-baidu-blue.png │ ├── carm-baidu-blue.psd │ ├── index.html │ └── tx.png │ ├── go.mod │ ├── go.sum │ └── main.go ├── lesson10 ├── Dockerfile ├── docker-compose.yaml ├── docker.md ├── go.mod ├── go.sum └── main.go ├── lesson11 ├── README.md └── images │ ├── GOLint.png │ ├── RESTful.png │ ├── bilibili1.PNG │ ├── bilibili2.png │ ├── bilibili3.png │ ├── blue_cat_worm.jpeg │ ├── demon.png │ ├── flow_chart.PNG │ ├── real_instance.png │ ├── structure1.png │ ├── structure2.png │ ├── structure3.png │ ├── structure_ability.png │ ├── structure_example.png │ ├── system_relations.png │ ├── system_user.png │ ├── tree.png │ ├── viper.PNG │ └── zap.PNG ├── lesson12 ├── images │ ├── 1.png │ ├── 10.png │ ├── 11.png │ ├── 12.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ ├── 7.png │ ├── 8.webp │ └── 9.webp ├── main.go └── 消息队列.md ├── lesson13 └── lesson13.md ├── lesson14 └── lesson14.md ├── lesson15 ├── README.md └── images │ ├── 1280X1280 (1).PNG │ ├── 1280X1280.PNG │ ├── 398cdbe8-1006-48b3-bba6-b73768c02aca.png │ ├── 4e760b81-7bfb-437a-83f7-c054ea6b414c.webp │ ├── 7f37cdb4-faa1-4341-9daa-9f24646f4443.png │ ├── 930904ca-0525-41af-b5e5-314fd377dfce.png │ ├── af08e7a2-c965-4084-a417-ada9ee07d899.webp │ ├── caec8684-c117-4521-a6bd-a124a795e819.webp │ ├── d0a32a7c-8b86-4083-a156-4570db6f3018.png │ ├── dfbb50b0-6a41-4045-9e47-6a77ffc2d5b3.png │ └── java_error_in_idea.png ├── lesson16 └── README.md ├── lesson17 ├── img │ ├── agent.png │ ├── component.png │ ├── gateway.png │ ├── jaeger.png │ ├── nocollector.png │ ├── otel_strcut.png │ ├── pipeline.png │ ├── prometheus.png │ ├── trace.png │ ├── trace1.png │ ├── trace2.png │ ├── trace3.png │ ├── trace4.png │ ├── trace5.png │ └── trace_collector.png └── 后端 Go 第十七次课.md └── lesson18 ├── images ├── cap.png ├── leader_turn.png ├── snowflake.png └── tcc.png └── 分布式基础.md /.github/workflows/ci-cd.yml: -------------------------------------------------------------------------------- 1 | name: Docker Build and Push a Simple # 工作流程的名称 2 | 3 | on: 4 | workflow_dispatch: # 通过 workflow_dispatch 手动触发工作流程 5 | inputs: 6 | parameter_name: 7 | description: 'go' 8 | required: true 9 | 10 | jobs: 11 | build-and-push-hello: # 构建和推送 hello 的作业 12 | runs-on: ubuntu-latest # 在 Ubuntu 最新版本上运行 13 | 14 | steps: 15 | - name: Checkout the repository # 检出存储库 16 | uses: actions/checkout@v2 17 | 18 | - name: Login to Docker Hub # 登录到 Docker Hub 19 | uses: docker/login-action@v1 20 | with: 21 | username: ${{ secrets.DOCKER_USERNAME }} # 使用存储库的 Docker 用户名 22 | password: ${{ secrets.DOCKERHUB_TOKEN }} # 使用存储库的 Docker 访问令牌 23 | 24 | - name: Set up QEMU # 设置 QEMU 25 | uses: docker/setup-qemu-action@v1 26 | 27 | - name: Set up Docker Buildx # 设置 Docker Buildx 28 | uses: docker/setup-buildx-action@v1 29 | 30 | - name: Create and push chat-rpc Docker image # 创建并推送 chat-rpc Docker 镜像 31 | uses: docker/build-push-action@v2 32 | with: 33 | context: . # Docker 上下文路径 34 | file: ./lesson10/docker-compose.yaml # Dockerfile 文件路径 35 | push: true # 推送镜像到 Docker Hub 36 | tags: liuxian123/hello:latest # 镜像的标签 37 | platforms: linux/amd64,linux/arm64 # 构建多个架构的镜像 38 | 39 | - name: executing remote ssh commands using password # 使用密码执行远程 SSH 命令 40 | uses: appleboy/ssh-action@v0.1.10 41 | with: 42 | host: ${{ secrets.HOST }} # SSH 主机 43 | username: ${{ secrets.USERNAME }} # SSH 用户名 44 | password: ${{ secrets.PASSWORD }} # SSH 密码 45 | port: ${{ secrets.PORT }} # SSH 端口 46 | script: | 47 | cd /home/project/docker_test # 执行远程服务器上的命令,此处进入特定目录 48 | docker-compose stop hello 49 | docker-compose rm -f hello 50 | docker image rm liuxian123/hello:latest 51 | docker-compose up -d hello 52 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Courseware-Backend-Go-2023 2 | 3 | 蓝山工作室 && 勤奋蜂工作室 Go 方向 2023-2024年课件 4 | 5 | 第一学期:2023.10.21(第7周)- 2024.1.7(第18周)(暂定) 6 | 7 | | 节数 | 课程 | 说明 | 讲师 | 8 | | :--: | :------------------------: | :----------------------------------------------------------: |:---:| 9 | | 0 | 通识课 | 提问的智慧,学会用 chatgpt,typora,安装 jetbrains 以及 goland,等等 | 万子渝 | 10 | | 1 | go 基础语法(1) | 变量,常量,基本数据类型,运算符,流程控制,函数基础,fmt包 | 邓卓 | 11 | | 2 | go 基础语法(2) | 数组,切片,map,函数进阶,git | 刘力延 | 12 | | 3 | struct、指针、interface | 包,struct、指针、interface | 王丹力 | 13 | | 4 | 常见的库与初识开源 | strconv,time,os,context,(template),(flag) / 了解 github 开源 | 梁伟健 | 14 | | 5 | goroutine、channel、select | go 语言并发编程 | 赵锡军 | 15 | | 6 | web 开发 | net/http 库,gin,cookie,session,jwt | 廖淑娴 | 16 | | 7 | mysql | 基本概念,sqlx,gorm | 王俊杰 | 17 | | 8 | redis,mongodb | 基本概念,缓存策略,go 操作 redis 和 mongodb | 单培民 | 18 | | 9 | linux | linux 常见命令,部署项目等 | 杨璟轩 | 19 | | 10 | docker | docker概念,docker部署项目,编写dockerfile | 刘显 | 20 | | 11 | 单体项目实战 | zap,viper,gin/hertz 框架实战 | 万子渝 | 21 | 22 | 23 | 24 | 第二学期:第一周 —— 第九周 25 | 26 | | 节数 | 课程 | 说明 | 讲师 | 27 | | ---- | ---------------- | --------------------------------------------------------- | ------ | 28 | | 1 | 消息队列 | 消息队列的应用,常见消息队列解决方案 | 王丹力 | 29 | | 2 | 微服务(1) | 微服务架构,服务拆分原则,服务间通信,服务间发现机制 | 王俊杰 | 30 | | 3 | 微服务(2) | Go-zero 快速搭建微服务 demo | 刘显 | 31 | | 4 | 微服务(3) | Kitex 快速构建微服务 demo | 万子渝 | 32 | | 5 | API网关 | 负载均衡,限流、熔断、降级,Nginx、Traefik、Kong | 刘力延 | 33 | | 6 | 可观测性 | Log、Traces、Metrics,OTEL | 梁伟建 | 34 | | 7 | 分布式数据一致性 | 分布式 ID,分布式锁,分布式事务,一致性共识算法,配置中心 | 赵锡军 | 35 | | 8 | k8s | k8s 架构,常见 k8s 资源 | 邓卓 | 36 | | 9 | 杂项 | CICD,八股,杂项 | 万子渝 | 37 | 38 | -------------------------------------------------------------------------------- /lesson00/images/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson00/images/1.png -------------------------------------------------------------------------------- /lesson00/images/image-20231007213757592.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson00/images/image-20231007213757592.png -------------------------------------------------------------------------------- /lesson00/images/image-20231007213835122.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson00/images/image-20231007213835122.png -------------------------------------------------------------------------------- /lesson00/images/image-20231007213951785.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson00/images/image-20231007213951785.png -------------------------------------------------------------------------------- /lesson00/images/image-20231007214308703.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson00/images/image-20231007214308703.png -------------------------------------------------------------------------------- /lesson00/images/image-20231007214410280.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson00/images/image-20231007214410280.png -------------------------------------------------------------------------------- /lesson00/images/image-20231007214554512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson00/images/image-20231007214554512.png -------------------------------------------------------------------------------- /lesson00/images/image-20231007215945173.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson00/images/image-20231007215945173.png -------------------------------------------------------------------------------- /lesson00/images/image-20231008104402198.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson00/images/image-20231008104402198.png -------------------------------------------------------------------------------- /lesson00/images/image-20231008105637726.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson00/images/image-20231008105637726.png -------------------------------------------------------------------------------- /lesson00/images/image-20231008110110760.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson00/images/image-20231008110110760.png -------------------------------------------------------------------------------- /lesson00/images/image-20231008123718393.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson00/images/image-20231008123718393.png -------------------------------------------------------------------------------- /lesson00/从 0 开始学 Go.md: -------------------------------------------------------------------------------- 1 | # 从 0 开始学 Go 2 | 3 | 接下来我会教大家如何成为一个入门级别的 Go 软件工程师,这个教程里面不会包含如何敲代码,但是包括了一些软件工程师的基本素养(很多都是代代相传的 4 | 5 | 因为是面向 0 基础写的教程,所以很多地方比如说安装软件我会写的很细,以后的课程中可能就不会有这么细了 6 | 7 | 不论你是否有基础相信都能有所收获 8 | 9 | ## 前言 10 | 11 | 如果你想问我想学好互联网开发,当一个成功的码农最重要的是什么 12 | 13 | 我觉得首先很重要的是**探索精神** 14 | 15 | 1. 好奇心:探索精神源于好奇心,即对世界和知识的渴望。好奇心驱使人们主动寻求新的经验、知识和理解,不满足于现有的答案和常规的思维模式。它激发人们对问题的提出、现象的解释和现实的改变。 16 | 2. 探索未知:探索精神鼓励人们主动面对未知领域和挑战。它推动人们跨越舒适区,积极探索新的领域、新的观点和新的经验。探索者愿意接受风险和不确定性,勇于面对失败和困难,以获取新的知识和发现。 17 | 3. 创新思维:探索精神促使人们采用创新的思维方式来解决问题和应对挑战。它鼓励人们打破传统的思维模式,尝试新的方法和观点,挖掘新的可能性。创新思维涉及跳出传统边界、建立连接和寻找新的解决方案。 18 | 4. 持续学习:探索精神与持续学习密切相关。探索者认识到知识和经验的不断积累对于个人成长和发展的重要性。他们愿意持续学习新的技能、学科和领域,并将学习应用于实践中,以推动个人和社会的进步。 19 | 20 | 是不是想起了玩游戏🎮时的经历? YES!你完全可以带入到开发学习中 21 | 22 | > "程序员贪婪又懒惰"——《Unix编程艺术》(The Art of Unix Programming) 23 | 24 | 然后最重要的是 **坚持** ✊ 25 | 26 | 还记得每次上课都听的迷迷糊糊的,这里为什么会报错?并发为什么每次效果不一样?`interface` 有什么用?`Context.context`是干啥的?为什么一开始要写个 `r.Register()`?会在学习的过程中遇到数不清的问题,跳进数不清的坑。但是只要坚持你回过来看发现其实都很简单,每个大佬也都是从 rookie 开始的嘛 27 | 28 | 碰到问题不可怕,找到解决问题的方式是关键,于是就引入了下一小节——**提问的智慧** 29 | 30 | ## 提问的智慧 31 | 32 | 详见 https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/main/README-zh_CN.md 33 | 34 | > 直接引用 21 级学长写的文档 🙏 35 | 36 | 你们或许在提问题的时候觉得我们很傲慢? 37 | 38 | ### 🐂🐎问题 39 | 40 | - 手机拍代码 41 | - 只说一个笼统的问题,不说细节 42 | - 夺命连环问 43 | 44 |  45 | 46 | [](https://github.com/LanshanTeam/Courseware-Backend-Go-2022/blob/main/class00-通识课/image/a277d2d2f89d0a22f4b12e425b585b2fcf547ede587345840.jpg) 47 | 48 | ### 例子 49 | 50 | **你以为的傲慢**: 51 | 52 | - 学弟:学长我vscode装不上了怎么办? 53 | - 学长:你到官网下载安装包下载完双击它,一直点下一步就安装好了 54 | - 学弟:官网是什么 55 | - 学长:https://code.visualstudio.com/ 56 | - 学弟:下载按钮在哪? 57 | - 学长:(截图) 58 | - 学弟:好了,下载到了,然后呢? 59 | - 学长:这个都不会还是别学了 60 | - 学弟:(感觉学长很傲慢)... 61 | 62 | **实际上的傲慢:** 63 | 64 | - 学弟:学长我的电脑是64位的windows 10,我在vscode官网下载 了对应版本,在我安装过程中出现了这个问题,使用百度搜索后还是没有办法解决,你能帮我看看吗?谢谢。(同时 65 | - 贴上安装失败的图片) 66 | - 学长:这个都不会还是别学了 67 | 68 |  69 | 70 | **再举个🌰** 71 | 72 | 某论坛 73 | 74 | - A:有人有赛博朋克2077游戏资源吗? 75 | - B:有,给https://pan.baidu.com/xxxxxxxxxx 76 | - A:为啥要用百度网盘啊,我没用过 77 | - B:只有这个 78 | - A:点击哪里下载啊 79 | - B:(截了个图) 80 | - A:哦哦 81 | - 过了几个小时 82 | - A:为什么下下来不能直接玩? 83 | - B:这是个压缩包,你要解压 84 | - A:唉,我玩个游戏也太麻烦了,有什么方式能解压吗 85 | 86 | 是不是能理解了呢😄 87 | 88 | ### 我们为什么对你们提的问题感到厌烦? 89 | 90 | - 提的问题“过于简单“ 91 | - 提问题的方式不对 92 | - 遇到问题就提问 93 | 94 | ### 问问题要讲究策略 95 | 96 | 你可以根据以下顺序尝试解决你的问题: 97 | 98 | 1. 百度/Google 99 | 2. 问 chatgpt 100 | 3. 项目使用文档、API使用说明 101 | 4. CSDN/Stack Overflow 102 | 5. 尝试分析源码 103 | 104 | ### 怎么问问题? 105 | 106 | - 精确地描述问题并言之有物 107 | 108 | - 仔细、清楚地描述你的问题或者Bug的症状 109 | - 描述问题发生的环境 110 | - 描述在提问前你是怎样去研究和解决这个问题的 111 | - 描述在提问前你为了确定问题而采取的诊断步骤 112 | - 尽可能地提供一个可以`重现这个问题的可控环境`的方法 113 | 114 | - 话不在多,在于精 115 | 116 | 你需要提供精确有内容的信息 117 | 118 | 这并不是要求你简单的就把成堆的错误代码或者资料完全放在你的提问中,如果你的问题是一个很大的是程序挂掉的这样一个代码运行环境,尽量把它剪裁得越小越好。 119 | 120 | 这样做的好处至少有三点: 121 | 122 | - 第一,表现你为简化问题付出了努力,这可以使你得到回答问题的概率增加 123 | - 第二,简化问题使你更有可能得到有用的答案 124 | - 第三,在精炼你的 bug 报告的过程中,你很可能就自己找到了解决方法或者权宜之计 125 | 126 | - 清楚明确地表达你的需求 127 | 128 | 漫无边际的提问是近乎无休无止的时间黑洞。 129 | 130 | 最有可能最有能力给你有用答案的人通常也是最忙的人(他们忙是因为要亲自完成大部分工作)。所以我们对这样无休止无节制的时间黑洞是相当厌恶的。 131 | 132 | 所以,请界定一下你的问题,使我们花在辨识你的问题和回答所需要付出的时间减到最少。 133 | 134 | - 询问有关代码的问题时 135 | 136 | 千万不要动辄就要求别人帮你去调试有问题的代码,而且也不提示一下应该从何入手。 137 | 138 | 张贴几百行的代码,然后说一声:`这段代码有问题`,我们可能完全会忽略这样的问题,而且回都不想回 139 | 140 | 相比于这个,只贴几十行的代码,然后说一句:`在第七行代码之后,我觉得程序会输出xxx,但实际出现的xxx`更有可能让你得到回应 141 | 142 | 最有效描述程序代码问题的方法就是提供一段最精简的 Bug 展示的测试用例。 143 | 144 | 什么是最精简的测试用例?那是问题的缩影;就是一小个代码片段能够刚好的展示出程序的异常行为,而不包含其他令人分散注意力的代码的内容。 145 | 146 | 怎么才能找出最精简的测试用例?如果你知道哪一行或者哪一段代码会造成异常的行为,复制下来并且加入能够重现这个状况的代码(能让这段异常的代码正常运行)。如果你无法将问题缩减到一个特定区块,就复制一份代码并移除不影响产生问题行为的部分。总之,你发出的测试用例(或代码)越少越好。 147 | 148 | - 截图的方式 149 | 150 | 不要手机拍照!不要手机拍照!不要手机拍照! 151 | 152 | - 有礼貌地提问 153 | 154 | 多用`请`和`谢谢你的回答`,让回答者知道你对他们能够花费掉自己宝贵地时间来对你提供帮助心存感激。 155 | 156 | 157 | 158 | 当然,对于很多问题大家都是 0 基础,问很正常,我们还是很乐意大家提问的 159 | 160 | ## 安装软件以及一些工具的使用 161 | 162 | ### 一些懂的都懂的东西 163 | 164 | 为什么我 github 只能看运气进?为什么我 git 老是推不上去?为什么我项目拉不下来? 165 | 166 | 这些问题都是懂得都懂的,除了换镜像源之外该怎么解决呢? 167 | 168 | 挂加速器可以解决一时之需,无论是游戏加速器还是「Watt Toolkit」,都能加速 github 等一些学术网站 169 | 170 | 如果你之后有更深的需求,这里不方便说,来私聊群里的学长学姐 171 | 172 | ### 学会使用 ChatGPT 173 | 174 | ChatGPT 在22年12月初才正式进入公众视野,23年初才广为人知,但是可以说已经给程序员带来了天翻地覆的影响 175 | 176 | 除了可以用来完成水课作业,还可以对初学者起到非常大的帮助,我觉得应该可以解决 90% 的问题吧 177 | 178 | 首先选择一个环境,我其实不太推荐官网,因为官方注册账号有门槛,很多节点被墙,经常抽风。所以我比较推荐一些镜像源网站比如[这个](https://poe.com/) 179 | 180 | 登上这个网站只需要任意一个梯子就行,其次 gpt-3.5 模型是免费的,还支持其他的很多模型 181 | 182 | 像这样,你可以向它提出问题,也可以喊它给出某个功能的代码,也可以直接复制粘贴报错信息看是什么原因 183 | 184 |  185 | 186 | 但是切记不可依赖 gpt,你会发现它很多时候还是很蠢的(比如学校的题很多都做不起),可以拿来提高效率但是不能让它替代你的工作 187 | 188 | ### Github 189 | 190 | 什么是 github ? 191 | 192 | GitHub是一个面向开发者的基于Web的版本控制和代码托管平台。它提供了一个集中存储、管理和协作开发代码的平台,使开发者能够更好地进行团队协作、版本控制和代码管理。 193 | 194 | GitHub的主要功能包括: 195 | 196 | 1. 代码托管:开发者可以将他们的代码库(仓库)上传到GitHub上进行存储和管理。这样可以确保代码的备份和安全,并且可以方便地与其他开发者共享和协作。 197 | 2. 版本控制:GitHub使用Git作为其主要的版本控制系统。Git可以追踪代码的修改历史,并提供了分支(branch)和合并(merge)等功能,使得多人协作和代码管理更加灵活和高效。 198 | 3. 协作开发:GitHub提供了丰富的协作功能,开发者可以通过提出问题(issue)、进行讨论、提交请求(pull request)等方式与团队成员交流和合作。这使得多人协作开发更加便捷和透明。 199 | 4. 社交网络:GitHub具有社交网络的特性,开发者可以关注其他开发者、关注和点赞感兴趣的项目,以及参与开源社区的活动。这种社交互动促进了知识共享、技术交流和合作机会。 200 | 5. 自动化工具:GitHub支持与其他开发工具和服务的集成,例如持续集成(Continuous Integration)和部署(Deployment)工具,可以通过自动化流程来构建、测试和部署软件。 201 | 202 | GitHub在开源社区和商业开发中都非常受欢迎。许多开源项目都托管在GitHub上,使得开发者可以共享代码、解决问题和贡献代码。同时,许多企业也使用GitHub作为内部代码托管和团队协作的平台,提高了开发效率和代码质量。 203 | 204 | 现在让我们打开 github https://github.com/ 205 | 206 | 点击右上角 Sign up 创建一个账户 207 | 208 |  209 | 210 | 填写自己的邮箱、密码、用户名等信息,然后用邮箱验证即可完成。 211 | 212 |  213 | 214 | 是不是全是英文感觉看不懂🥺 215 | 216 | 我的建议是,习惯就好🤣,以后会经常碰到英文文档的 217 | 218 | 然后点击右上角头像之后点击仓库 219 | 220 |  221 | 222 | 点击这里新建你的第一个仓库 223 | 224 |  225 | 226 | 这些先简单填一点,开源证书什么的可以暂时不用管,然后点击创建仓库,那么以后就可以在把你的作业代码交到这个仓库里啦 227 | 228 |  229 | 230 | ### JetBrains 231 | 232 | JetBrains是一家软件开发工具公司,专注于提供高效、智能的集成开发环境(IDE)和开发者工具。他们的产品被广泛应用于各种编程语言和技术栈的开发中。 233 | 234 | 除了马上会提到的 Goland,像 C语言可以用 Clion,Python 可以用 PyCharm,java 可以用 IntelliJ IDEA 235 | 236 | 如何下载? 237 | 238 | 看看下面的博客👇里面提到的学生认证可以不用急着弄 239 | 240 | https://blog.csdn.net/qq_25887493/article/details/124106216?ops_request_misc=&request_id=&biz_id=102&utm_term=%E5%AE%89%E8%A3%85%20jetbrains&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-1-124106216.142^v95^chatgptT3_1&spm=1018.2226.3001.4187 241 | 242 | ### Goland 243 | 244 | 可能有同学会问,为啥用 JetBrains 的 Goland ,不用 vscode 245 | 246 | 我直接粘贴 chatgpt 了 247 | 248 |  249 | 250 | 可以在 JetBrains 里的 ToolBox 里直接快速安装 251 | 252 | 想直接下载? 253 | 254 | 看看下面的博客👇 255 | 256 | https://blog.csdn.net/Cappuccino_jay/article/details/129466743?ops_request_misc=&request_id=&biz_id=102&utm_term=%E5%AE%89%E8%A3%85%20goland&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-0-129466743.142^v95^chatgptT3_1&spm=1018.2226.3001.4187 257 | 258 | ### Git 259 | 260 |  261 | 262 |  263 | 264 | 省流:Git 是目前世界上最先进的分布式版本控制系统,用来团队协作很方便。就不用每次上传代码都靠手动 upload files 了 265 | 266 | 267 | 268 | 安装教程看这个👇 269 | 270 | https://blog.csdn.net/Passerby_Wang/article/details/120767020?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522169673342216800182730025%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=169673342216800182730025&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-4-120767020-null-null.142^v95^chatgptT3_1&utm_term=%E5%AE%89%E8%A3%85%20git&spm=1018.2226.3001.4187 271 | 272 | 然鹅我们在用 git 的时候基本上不会在 git 的客户端里敲命令,要么用 IDE 的图形化工具要么用 IDE 的命令行 273 | 274 |  275 | 276 | 使用教程后面第二节课会详细讲,感兴趣的同学可以先看下面这个教程👇 277 | 278 | https://blog.csdn.net/weixin_55387973/article/details/123849753?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522169673357916800226593163%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=169673357916800226593163&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-123849753-null-null.142^v95^chatgptT3_1&utm_term=goland%20git&spm=1018.2226.3001.4187 279 | 280 | 感觉教程看不懂?那来玩下 git 闯关小游戏吧👇 281 | 282 | https://learngitbranching.js.org/?demo=&locale=zh_CN 283 | 284 | ### Typora 285 | 286 | #### 什么是markdown? 287 | 288 | Markdown是一门标记语言。假如你们知道HTML的话,Markdown和HTML可以认为是同类事物。简而言之,就是用纯文本的代码描述带格式文本的一种方法。 289 | 290 | 那么有了HTML,要Markdown有何用?最简单的答案是:HTML太难写了! 291 | 292 | 这样对于关注内容的作者而言友好了很多,而且即使是Markdown代码也有相当强的可读性。 293 | 294 | 于是写好Markdown后,如何将其显示成我们想要的格式呢?通常的做法是将Markdown编译为HTML。其实一些网站已经自带了这样的编译器,输入Markdown代码后直接将其转化为HTML,就可以交给浏览器渲染了。GitHub上的README以及各种.md格式文件的预览功能就是最典型的例子。 295 | 296 | Markdown 编写的文档后缀为 `.md`, `.markdown` 297 | 298 | #### 什么地方会用到markdown? 299 | 300 | Markdown可以广泛用于文档、博客、论坛等带格式文本内容的创作,习惯后使用起来会比所见即所得的HTML编辑器更加方便快捷,较Word等格式又有纯文本这一优势。 301 | 302 | #### typora又是啥 303 | 304 | 而 typora 就是一款非常方便的支持 md 格式的笔记软件,有很多功能:实时预览、数学公式、代码高亮、表格、支持HTML、流程图等等 305 | 306 | 这份文档就是在 typora 里敲出来的,反正我个人大多数情况下觉得比 word 好用多了 307 | 308 | 官网下载就完事了https://typora.io/ 309 | 310 | ## 如何高效学习 311 | 312 | > 我除了跟着学长学姐学习之外还能在哪里自学呢? 313 | 314 | 有这种自学的意识是非常好的,到了大学之后自驱力是成为强者的必要特质 315 | 316 | 接下来我会介绍一些学习心得 317 | 318 | ### 入门 Go 的一些资源 319 | 320 | 贪多嚼不烂,先推荐一些基础的: 321 | 322 | [李文周的博客](https://www.liwenzhou.com/posts/Go/golang-menu/) 323 | 324 | [go 语言圣经](https://books.studygolang.com/gopl-zh/) 325 | 326 | 还有一本难度很大的书,推荐学过数据结构,操作系统之后再来看 327 | 328 | 《Go 程序设计语言》 329 | 330 | ### Coding 能力 331 | 332 | #### 网站资源 333 | 334 | 这个是覆盖面积最广的,也是质量最参差不齐的 335 | 336 | 首先是 [**CSDN**](https://www.csdn.net/),这个东西很抽象,应该是国内最老牌的技术交流网站,但是正因如此质量参差不齐,有很多灌水,缝合,抄袭的文章。所以说在上面找资源等于 *里掏金,当然对于初学者而言还是很有帮助的 337 | 338 | 然后是字节搞的[稀土掘金](https://juejin.cn/),观感比CSDN好多了,但是也有缺陷就是有时候想搜的内容在上面搜不到 339 | 340 | 比较推荐的是 [stackoverflow](https://stackoverflow.com/),全世界知名的技术交流论坛,但是对英语能力有一定要求 341 | 342 | 然后可能有同学想看视频资源,b站也是个不错的选择,但是质量更是参差不齐,我觉得大部分看文档就能理解没必要去浪费时间看视频,除非视频资源真的讲的很好。其他诸如极客时间等等就不一一介绍了 343 | 344 | #### 学会看官方文档 345 | 346 | 官方文档其实可以说是了解一个组件最高效的方式,在有一定编程基础后看官方文档入门是不二之选 347 | 348 | 以下图为例,这是 go 的官方文档,介绍了包含的所有官方库以及其他功能 349 | 350 |  351 | 352 | 很多组件是国外开发的,因此也是英文的,看懂其实并不难,只是一开始会有畏难情绪,能过四级就没多大阅读障碍了 353 | 354 | 如果实在看起觉得恼火可以找找有没有中文镜像站或者直接整页翻译 355 | 356 | #### 学会看源码 357 | 358 | 这个估计已经是很高级的能力了,与 Java 能用到退休的教程和生态相比,Go虽然也不是很年轻了,但是肯定也是会遇到没有教程的时候。 359 | 360 | 这时候咋办?看源码! 361 | 362 | 而且看源码还能提高你的 Coding 能力,在找工作面试的时候面试官也很有可能问到 go 的一些常见底层实现。例如看到某个库的教程觉得讲的太烂了,这个时候就可以去看看相应的源码 363 | 364 | 除此之外学习也是从“抄袭”开始的,抄的是代码逻辑,排版风格...然后把这些东西融会贯通贯通成为自己的,GitHub上就有很多开源项目供大家学习 365 | 366 | ### 时间管理能力 367 | 368 | > 觉得上了大学之后没有想象中那么轻松?觉得一周里课太多?觉得事情很多忙不过来? 369 | 370 | 这也是很多同学大一时会犯的通病 371 | 372 | 首先我们需要明确一个前提:**靠学校教的东西以后出去吃不了饭** 373 | 374 | > 如果你的目标是本科毕业直接就业,那么你绝对需要自己好好规划时间 375 | 376 | 首先说本科毕业就业,那么你对绩点就不需要那么看重了,每科能过就行 377 | 378 | 就大一上而言,需要重视的就是高数和线代。难道C语言就不重要了吗?当然很重要,重要的是它是很多人接触的第一门编程语言,能培养编程思维。说C语言不需要重视是因为学校里教的很水,拿我自己举例子,我暑假学了一点C,上课一节课没听最后期末总成绩都能上90。也就是说有这时间听老师上课讲不如自己自学。高数和线代要好好学,要不然期末突击也不太好过。 379 | 380 | 因此可以得出除了高数和线代都是水课,一周加起来也才五节课,这么来看是不是就轻松很多了呢。其他时间可以狠狠地拿来学编程了,老师管的严的把电脑搬到最后一排坐着;管的不严的emm。。你懂的 381 | 382 | > 那我想保研或者考研咋办呢 383 | 384 | 考研的话其实和上面差别不大,只是不能抱着只是为了通过考试的心态了,要正儿八经好好学**想考专业的专业课**。 385 | 386 | 但是保研的话就不一样了,我反正觉得挺坐牢的😭,每门课你都得认真对待,平时分考试分都不能落下。上课该回答问题就回答问题🙋 387 | 388 | #### 竞赛 389 | 390 | 以下仅代表我个人观点 391 | 392 | 竞赛的重要性排序依次是:保研>考研>工作,但是并不绝对 393 | 394 | 保研你没有拿的出手的东西估计只有保本校了,工作的话很多奖人家不是很看重,只是锦上添花。 395 | 396 | 然后我们再来谈谈种类,我**个人**比较倾向于分为这几类:**计算机设计类、算法类、创新创业类、数模、其他类** 397 | 398 | ##### 计算机设计类 399 | 400 | 这个我觉得对于我们搞开发的是最好打的,几个人组个小队,搓个项目,碰到比赛交上去就行。 401 | 402 | 有学长拿他们的项目打了很多比赛都取得了很好的成绩 403 | 404 | ##### 算法类 405 | 406 | 这个东西下限差不多就是蓝桥杯的水平,上限应该是ACM的水平 407 | 408 | 平时没事多练习蓝桥杯还是很好拿奖的。但是ACM就非常有难度了,需要投入大量精力并且可能对天赋也有一定要求,没有专门训练过就是去送 409 | 410 | ##### 创新创业类 411 | 412 | 就是大创、互联网+之类的。比赛周期比较长,跟开发关联其实不是很大,PPT制作、项目策划书以及答辩比较重要。如果真要搞这个建议找靠谱的老师组个团队带你打 413 | 414 | ##### 数模类 415 | 416 | 跟算法类比较相似,也有一定门槛。想拿奖也需要投入精力时间 417 | 418 | ##### 其他类 419 | 420 | 像什么大数据、物联网、嵌入式、机器人、邮政、医疗等等就不一一介绍了,关联度不是很大 421 | 422 | ## 后记 423 | 424 | **选择大于努力** 425 | 426 | That's all 427 | 428 | -------------------------------------------------------------------------------- /lesson01/蓝山工作室——Golang第一节课.md: -------------------------------------------------------------------------------- 1 | # 蓝山工作室——Golang第一节课 2 | 3 | ## 前言 4 | 5 | 首先,我要热烈欢迎你们加入我们的Go语言课程。Go语言是一门强大且灵活的编程语言,它具备高性能、简单易学的特点,已经在许多领域取得了广泛的应用。本课程将帮助你掌握Go语言的核心概念、语法和最佳实践,无论你是初学者还是有一些编程经验的学员。 6 | 7 | Go语言是一门开源编程语言,由Google开发并维护。它的设计目标是简单性、效率和可读性,使得开发者可以更轻松地构建高性能的应用程序。Go语言在云计算、网络编程、大数据处理等领域表现出色,是许多知名公司和大厂的首选语言之一。 8 | 9 | 这节课的主要内容是认识:变量,常量,基本数据类型,运算符,流程控制,函数基础,fmt包 10 | 11 | ## 基础语法 12 | 13 | ### var 14 | 15 | #### 变量 16 | 17 | ##### 变量类型 18 | 变量(Variable)的功能是存储数据。不同的变量保存的数据类型可能会不一样。经过半个多世纪的发展,编程语言已经基本形成了一套固定的类型,常见变量的数据类型有:整型、浮点型、布尔型等。 19 | 20 | Go语言中的每一个变量都有自己的类型,并且变量必须经过声明才能开始使用。 21 | 22 | 1. **整数类型(Integer Types)**: 23 | - `int`:根据你的操作系统架构,可以是32位或64位的整数。 24 | - `int8`、`int16`、`int32`、`int64`:有符号整数类型,分别表示8位、16位、32位和64位整数。 25 | - `uint`:无符号整数类型,根据操作系统架构,可以是32位或64位。 26 | - `uint8`、`uint16`、`uint32`、`uint64`:无符号整数类型,分别表示8位、16位、32位和64位整数。 27 | 2. **浮点数类型(Floating-Point Types)**: 28 | - `float32`:单精度浮点数。 29 | - `float64`:双精度浮点数。 30 | 3. **复数类型(Complex Types)**: 31 | - `complex64`:包含32位实部和32位虚部的复数。 32 | - `complex128`:包含64位实部和64位虚部的复数。 33 | 4. **布尔类型(Boolean Type)**: 34 | - `bool`:表示真(true)或假(false)。 35 | 5. **字符串类型(String Type)**: 36 | - `string`:用于存储文本数据的字符序列。 37 | 6. **字节类型(Byte Type)**: 38 | - `byte`:`uint8` 的别名,通常用于表示ASCII字符。 39 | 7. **符文类型(Rune Type)**: 40 | - `rune`:`int32` 的别名,通常用于表示**Unicode字符**。 41 | - Unicode(统一码、万国码、单一码)是一种字符编码标准,它用于表示世界上几乎所有的书写系统中的字符,**包括各种文字、标点符号和特殊符号**。Unicode的目标是提供一个统一的、跨语言的字符编码系统,以消除不同字符编码之间的混乱和兼容性问题。 42 | 43 | ##### 变量声明 44 | Go语言中的变量需要声明后才能使用,同一作用域内不支持重复声明。并且Go语言的变量声明后**必须使用**。 45 | 46 | ##### 变量的初始化 47 | Go语言在声明变量的时候,会自动对变量对应的内存区域进行初始化操作。每个变量会被初始化成其类型的默认值,例如: 整型和浮点型变量的默认值为0。 字符串变量的默认值为空字符串。 布尔型变量默认为false。 切片、函数、指针变量的默认为nil。 48 | 49 | ```go 50 | var 变量名 类型 = 表达式 51 | 52 | var a = "initial" // 类型推导,不指定类型自动判断 53 | 54 | var b, c int = 1, 2 // 一次初始化多个变量 55 | 56 | var d = true // 布尔型变量 57 | 58 | var e float64 // 普通声明未赋值 59 | 60 | f := float32(e) // 类型转换初始化赋值 61 | 62 | g := a + "apple" 63 | fmt.Println(a, b, c, d, e, f) // initial 1 2 true 0 0 64 | fmt.Println(g) // initialapple 65 | ``` 66 | 67 | #### 常量 68 | 69 | 相对于变量,常量是恒定不变的值,多用于定义程序运行期间不会改变的那些值。 常量的声明和变量声明非常类似,只是把var换成了const,常量在定义的时候必须赋值。 70 | 71 | ```go 72 | const s string = "constant" 73 | const h = 500000000 74 | const i = 3e20 / h 75 | fmt.Println(s, h, i, math.Sin(h), math.Sin(i)) 76 | ``` 77 | 78 | ### for 79 | 80 | for 是设置条件并让代码进行循环的关键字 81 | 82 | #### 三段式 83 | 84 | ```go 85 | for A; B; C { 86 | // 这里是循环体,每次循环需要执行的代码在这里面 87 | } 88 | ``` 89 | 90 | `A` 位置是单次表达式,循环开始时会执行一次这里,一般用于初始化变量。 91 | 92 | `B` 位置是条件表达式,即循环条件,只要满足循环条件就会执行循环体。 93 | 94 | `C` 位置是末尾循环体,每次执行完一遍循环体之后会执行一次 `C` 中的表达式。 95 | 96 | 执行末尾循环体后将再次进行条件判断,若条件还成立,则继续重复上述循环,当条件不成立时则跳出当下for循环。 97 | 98 | 并且,你可以选择性的留空,就是让A,B,C**任何位置**为空。例如: 99 | 100 | ```go 101 | for ;i<5;{ 102 | // 循环体 103 | } 104 | ``` 105 | 106 | #### 一段式 107 | 108 | 一段式那就是只写条件 109 | 110 | ```go 111 | for A{ 112 | // 循环体 113 | } 114 | ``` 115 | 116 | `A` 位置是条件表达式,即循环条件,只要满足循环条件就会执行循环体。 117 | 118 | 例如: 119 | 120 | ```go 121 | for i<5{ 122 | // 循环体 123 | } 124 | ``` 125 | 126 | ##### break 关键词 127 | 128 | break 放在循环体中,只要执行到 break,则会立马跳出所在最里循环(注意,是所在最里循环,若嵌套,则无法跳出更外层循环) 129 | 130 | 例如: 131 | 132 | ```go 133 | for i:=1;i<4;i++{ 134 | j := i 135 | for j<4{ 136 | if j == 2{ 137 | break 138 | } 139 | } 140 | fmt.Println("hello lanshan") 141 | } 142 | ``` 143 | 144 | 上面这个函数不需要去体会其中的意思,我只是举个例子。若执行到了break,则只会跳出条件是 j < 4 这个循环,依然会执行println打印 145 | 146 | 这是总的示例,可以回顾一下: 147 | 148 | ```go 149 | package main 150 | 151 | import "fmt" 152 | 153 | func main() { 154 | i := 1 155 | for { 156 | fmt.Println("loop") 157 | break // 跳出循环 158 | } 159 | 160 | // 打印7、8 161 | for j := 7; j < 9; j++ { 162 | fmt.Println(j) 163 | } 164 | 165 | for n := 0; n < 5; n++ { 166 | if n%2 == 0 { 167 | continue 168 | // 当n模2为0时不打印,进到下一次的循环 169 | } 170 | fmt.Println(n) 171 | } 172 | // 直到i>3 173 | for i <= 3 { 174 | fmt.Println(i) 175 | i = i + 1 176 | } 177 | // for 循环嵌套 178 | for i := 0; i < 5; i++ { 179 | for j := 0; j < 5; j++ { 180 | fmt.Printf("i = %d, j = %d\n", i, j) 181 | } 182 | } 183 | } 184 | ``` 185 | 186 | ### if 187 | 188 | ```go 189 | if 条件表达式 { 190 | //当条件表达式结果为true时,执行此处代码 191 | } 192 | 193 | if 条件表达式 { 194 | //当条件表达式结果为true时,执行此处代码 195 | } else { 196 | //当条件表达式结果为false时,执行此处代码 197 | } 198 | ``` 199 | 200 | ```go 201 | package main 202 | 203 | import "fmt" 204 | 205 | func main() { 206 | // 条件表达式为false,打印出"7 是奇数" 207 | if 7%2 == 0 { 208 | fmt.Println("7 是偶数") 209 | } else { 210 | fmt.Println("7 是奇数") 211 | } 212 | 213 | // 条件表达式为ture,打印出"8 被 4 整除" 214 | if 8%4 == 0 { 215 | fmt.Println("8 被 4 整除") 216 | } 217 | 218 | // 这是一个短声明,效果等效于 219 | //num := 9 220 | //if num < 0{ 221 | // ... 222 | //} 223 | if num := 9; num < 0 { 224 | fmt.Println(num, "is negative") 225 | } else if num < 10 { 226 | fmt.Println(num, "has 1 digit") 227 | } else { 228 | fmt.Println(num, "has multiple digits") 229 | } 230 | } 231 | ``` 232 | 233 | ### switch 234 | 235 | 当分支过多的时候,使用if-else语句会降低代码的可阅读性,这个时候,我们就可以考虑使用switch语句 236 | 237 | - switch 语句用于基于不同条件执行不同动作,每一个 case 分支都是唯一的,从上至下逐一测试,直到匹配为止。 238 | - switch 语句在默认情况下 case 相当于自带 break 语句,匹配一种情况成功之后就不会执行其它的case,这一点和 c/c++ 不同 239 | - 如果我们希望在匹配一条 case 之后,继续执行后面的 case ,可以使用 fallthrough 240 | 241 | ```go 242 | package main 243 | 244 | import ( 245 | "fmt" 246 | "time" 247 | ) 248 | 249 | func main() { 250 | 251 | a := 2 252 | switch a { 253 | case 1: 254 | fmt.Println("one") 255 | case 2: 256 | // 在此打印"two"并跳出 257 | fmt.Println("two") 258 | case 3: 259 | fmt.Println("three") 260 | case 4, 5: 261 | fmt.Println("four or five") 262 | default: 263 | fmt.Println("other") 264 | } 265 | 266 | t := time.Now() 267 | switch { 268 | // 根据现在的时间判断是上午还是下午 269 | case t.Hour() < 12: 270 | fmt.Println("It's before noon") 271 | default: 272 | fmt.Println("It's after noon") 273 | } 274 | } 275 | ``` 276 | 277 | ### func 278 | 279 | 函数是指一段可以直接被另一段程序或代码引用的程序或代码,一个较大的程序一般应分为若干个程序块,每一个模块用来实现一个特定的功能。 280 | 281 | 1. **函数的声明和定义**: 282 | 283 | 在Go语言中,函数的定义以 `func` 关键字开始,然后是函数名、参数列表、返回类型和函数体。以下是一个函数的典型定义: 284 | 285 | ```go 286 | func add(x int, y int) int { 287 | return x + y 288 | } 289 | ``` 290 | 291 | 这个函数名为 `add`,接受两个整数参数 `x` 和 `y`,并返回一个整数。 292 | 293 | 2. **函数的参数**: 294 | 295 | 函数可以接受零个或多个参数,参数在参数列表中定义,并且需要指定参数的类型。例如: 296 | 297 | ```go 298 | func greet(name string) { 299 | fmt.Println("Hello, " + name) 300 | } 301 | ``` 302 | 303 | 这个函数接受一个字符串参数 `name`。 304 | 305 | 3. **函数的返回值**: 306 | 307 | 函数可以返回一个或多个值,返回值的类型也需要在函数定义中指定。如果函数没有返回值,可以将返回类型留空。例如: 308 | 309 | ```go 310 | func addAndMultiply(x, y int) (int, int) { 311 | sum := x + y 312 | product := x * y 313 | return sum, product 314 | } 315 | ``` 316 | 317 | 这个函数返回两个整数值。 318 | 319 | 4. **函数的调用**: 320 | 321 | 要调用函数,只需使用函数名并传递参数。例如: 322 | 323 | ```go 324 | result := add(3, 5) 325 | fmt.Println(result) 326 | ``` 327 | 328 | 这里我们调用了 `add` 函数,将参数 `3` 和 `5` 传递给它,并将返回值赋给 `result` 变量。 329 | 330 | ```go 331 | package main 332 | 333 | import "fmt" 334 | 335 | func add(x int, y int) int { 336 | return x + y 337 | } 338 | 339 | func main() { 340 | result := add(3, 5) 341 | fmt.Println(result) 342 | } 343 | ``` 344 | 345 | ### fmt 346 | 347 | fmt 库函数 348 | 349 | ```go 350 | package main 351 | 352 | import "fmt" 353 | 354 | type point struct { 355 | x, y int 356 | } 357 | 358 | func main() { 359 | s := "hello" 360 | n := 123 361 | p := point{1, 2} 362 | fmt.Println(s, n) // hello 123 363 | fmt.Println(p) // {1 2} 364 | 365 | fmt.Printf("s=%v\n", s) // s=hello 366 | fmt.Printf("n=%v\n", n) // n=123 367 | fmt.Printf("p=%v\n", p) // p={1 2} 368 | fmt.Printf("p=%+v\n", p) // p={x:1 y:2} 369 | fmt.Printf("p=%#v\n", p) // p=main.point{x:1, y:2} 370 | 371 | f := 3.141592653 372 | fmt.Println(f) // 3.141592653 373 | fmt.Printf("%.2f\n", f) // 3.14 374 | } 375 | ``` 376 | 377 | ## 年轻人的第一个GoProject 378 | 379 | ## 作业 380 | 381 | ### LV1 382 | 383 | 编写一个Go函数,接受两个整数作为参数,然后返回它们的和。在 `main` 函数中调用此函数并打印结果。 384 | 385 | ### LV2 386 | 387 | 编写一个Go函数,接受圆的半径作为参数,然后返回圆的面积。使用 `math` 包中的常数 Pi。在 `main` 函数中调用此函数并打印结果。 388 | 389 | 提示,引入 Pi 只需要写出`math.Pi` 390 | 391 | ### LV3 392 | 393 | 编写一个Go函数,接受一个整数作为参数,然后判断它是否为素数(质数)。在 `main` 函数中调用此函数并打印结果。提示:一个素数是只能被 1 和自身整除的正整数。 394 | 395 | ### LVX 396 | 397 | 编写一个Go函数,使用`rand`包随机选择一个1-100的数(必须每次执行的随机数都不一样),然后使用**二分法**找到这个数。 398 | 399 | tips:rand 包的使用和二分法自行研究 400 | 401 | 402 | 作业完成后将作业 GitHub 地址发送至 **nihilism0@qq.com** ,若对 GitHub 的使用有问题,可以先网上寻找解决方法,实在不行可以私信我。 403 | 404 | **提交格式(主题):2023xxxx00-邓卓-LV3** (最后LV中写出你完成的最大等级) 405 | 406 | **截止时间**:下一次上课之前 407 | -------------------------------------------------------------------------------- /lesson02/02.md: -------------------------------------------------------------------------------- 1 | # 第二节课 2 | 3 | ## 数组-array 4 | 5 | > 数组是指一系列同一类型数据的集合。数组中包含的每个数据被称为数组元素(element),一个数组包含的元素个数被称为数组的长度。 6 | 7 | - 初始化 8 | 9 | ```go 10 | // var arr [len]type 11 | // arr := [len]type{data1,data2} 12 | a := [3]int{1, 2} // 未初始化元素值为 0 13 | b := [...]int{1, 2, 3} // 通过初始化值确定数组长度 14 | c := [5]int{2: 100, 4: 200} // 通过索引号初始化元素,未初始化元素值为 0 15 | fmt.Println(a, b, c) //[1 2 0] [1 2 3] [0 0 100 0 200] 16 | 17 | //支持多维数组 18 | d := [4][2]int{{10, 11}, {20, 21}, {30, 31}, {40, 41}} 19 | e := [...][2]int{{10, 11}, {20, 21}, {30, 31}, {40, 41}} //第二维不能写"..." 20 | f := [4][2]int{1: {20, 21}, 3: {40, 41}} 21 | g := [4][2]int{1: {0: 20}, 3: {1: 41}} 22 | fmt.Println(d, e, f, g) 23 | 24 | ``` 25 | 26 | > 数组⻓度必须是常量,且是类型的组成部分。 [2]int 和 [3]int 是不同类型。 27 | 28 | - 操作数组 29 | 30 | ```go 31 | // 数组的每个元素可以通过索引下标来访问,索引下标的范围是从0开始到数组长度减1的位置。 32 | var a [10]int 33 | for i := 0; i < 10; i++ { 34 | a[i] = i + 1 35 | fmt.Printf("a[%d] = %d\n", i, a[i]) 36 | } 37 | 38 | //range具有两个返回值,第一个返回值是元素的数组下标,第二个返回值是元素的值 39 | for i, v := range a { 40 | fmt.Println("a[", i, "]=", v) 41 | } 42 | 43 | ``` 44 | 45 | 46 | 47 | ## 切片-slice 48 | 49 | > 数组的长度在定义之后无法再次修改;数组是值类型,每次传递都将产生一份副本。显然这种数据结构无法完全满足开发者的真实需求。Go语言提供了数组切片(slice)来弥补数组的不足。 50 | 51 | - 初始化 52 | 53 | ```go 54 | //初始化一个空的切片 55 | var a1 []int //就比数组少个中括号里的长度 56 | a2 := []int{} 57 | 58 | //或者用make函数 59 | //make([]T, length, capacity) //capacity省略,则和length的值相同 60 | var a3 []int = make([]int, 0) 61 | a4 := make([]int, 0, 0) 62 | 63 | a5 := []int{1, 2, 3} //创建切片并初始化值 64 | ``` 65 | 66 | - 操作切片 67 | 68 |  69 | 70 | - 遍历 71 | 72 | ```go 73 | func main() { 74 | slice := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} 75 | //i是下标,v是元素的值 76 | for i, v := range slice { 77 | fmt.Printf("slice[%d]=%d, ", i, v) 78 | //slice[0]=-1, slice[1]=1, slice[2]=2, slice[3]=3, slice[4]=4, slice[5]=5, slice[6]=6, slice[7]=7, slice[8]=8, slice[9]=9, 79 | } 80 | fmt.Println("\n") 81 | } 82 | 83 | ``` 84 | 85 | - append( ) 86 | 87 | > append函数向 slice 尾部添加数据,返回新的 slice 对象: 88 | 89 | ```GO 90 | var s1 []int //创建nil切换 91 | //s1 := make([]int, 0) 92 | s1 = append(s1, 1) //追加1个元素 93 | s1 = append(s1, 2, 3) //追加2个元素 94 | s1 = append(s1, 4, 5, 6) //追加3个元素 95 | fmt.Println(s1) //[1 2 3 4 5 6] 96 | 97 | s2 := make([]int, 5) 98 | s2 = append(s2, 6) 99 | fmt.Println(s2) //[0 0 0 0 0 6] 100 | 101 | s3 := []int{1, 2, 3} 102 | s3 = append(s3, 4, 5) 103 | fmt.Println(s3)//[1 2 3 4 5] 104 | 105 | ``` 106 | 107 | > append函数会智能地底层数组的容量增长,一旦超过原底层数组容量,通常以2倍容量重新分配底层数组,并复制原来的数据 108 | 109 | ```GO 110 | func main() { 111 | var x, y []int 112 | for i := 0; i < 10; i++ { 113 | y = appendInt(x, i) 114 | fmt.Printf("%d cap=%d\t%v\n", i, cap(y), y) 115 | x = y 116 | } 117 | } 118 | ``` 119 | 120 | 输出: 121 | 122 | ```shell 123 | 0 cap=1 [0] 124 | 1 cap=2 [0 1] 125 | 2 cap=4 [0 1 2] 126 | 3 cap=4 [0 1 2 3] 127 | 4 cap=8 [0 1 2 3 4] 128 | 5 cap=8 [0 1 2 3 4 5] 129 | 6 cap=8 [0 1 2 3 4 5 6] 130 | 7 cap=8 [0 1 2 3 4 5 6 7] 131 | 8 cap=16 [0 1 2 3 4 5 6 7 8] 132 | 9 cap=16 [0 1 2 3 4 5 6 7 8 9] 133 | ``` 134 | 135 | - 切片和数组 136 | 137 | > 切片并不是数组或数组指针,它通过内部指针和相关属性引⽤数组⽚段,以实现变⻓⽅案。 138 | > 139 | > slice并不是真正意义上的动态数组,而是一个引用类型。slice总是指向一个底层array 140 | 141 | 142 | 143 |  144 | 145 | 146 | 147 | ## 字典-map 148 | 149 | > map是一种无序的基于key-value的数据结构 150 | > 151 | > 类似于我们高中学过的函数y=f(x) 152 | > 153 | > 每个x就会有一个与之对应的y 154 | > 155 | > 在map中,key就可以相当于x,value相当于y 156 | 157 | - 定义 158 | 159 | ```go 160 | // map[KeyType]ValueType 161 | // KeyType:表示键的类型,只能为基本类型。 162 | // ValueType:表示键对应的值的类型。 163 | var m1 map[int]string 164 | 165 | //或者用make定义 166 | //make(map[KeyType]ValueType, [cap]) 167 | //其中cap表示map的容量,该参数不是必须的 168 | m2 := make(map[int]string) 169 | m3 := make(map[int]string,10) 170 | ``` 171 | 172 | - 使用 173 | 174 | ```go 175 | func main() { 176 | scoreMap := make(map[string]int, 8) 177 | scoreMap["张三"] = 90 178 | scoreMap["小明"] = 100 179 | fmt.Println(scoreMap) 180 | fmt.Println(scoreMap["小明"]) 181 | fmt.Printf("type of a: %T\n", scoreMap) 182 | } 183 | // map[小明:100 张三:90] 184 | // 100 185 | // type of a: map[string]int 186 | ``` 187 | 188 | ```go 189 | // map也支持在声明的时候填充元素 190 | func main() { 191 | userInfo := map[string]string{ 192 | "username": "pprof.cn", 193 | "password": "123456", 194 | } 195 | fmt.Println(userInfo) 196 | } 197 | ``` 198 | 199 | - 遍历 200 | 201 | ```go 202 | m1 := map[int]string{1: "abc", 2: "def"} 203 | //迭代遍历1,第一个返回值是key,第二个返回值是value 204 | for k, v := range m1 { 205 | fmt.Printf("%d ----> %s\n", k, v) 206 | //1 ----> abc 207 | //2 ----> def 208 | } 209 | 210 | //迭代遍历2,第一个返回值是key,第二个返回值是value(可省略) 211 | for k := range m1 { 212 | fmt.Printf("%d ----> %s\n", k, m1[k]) 213 | //1 ----> abc 214 | //2 ----> def 215 | } 216 | 217 | //判断某个key所对应的value是否存在, 第一个返回值是value(如果存在的话) 218 | value, ok := m1[1] 219 | fmt.Println("value = ", value, ", ok = ", ok) //value = abc , ok = true 220 | 221 | value2, ok2 := m1[3] 222 | fmt.Println("value2 = ", value2, ", ok2 = ", ok2) //value2 = , ok2 = false 223 | 224 | ``` 225 | 226 | 227 | 228 | - 删除 229 | 230 | ```go 231 | m1 := map[int]string{1: "abc", 2: "def", 3: "gh"} 232 | //迭代遍历1,第一个返回值是key,第二个返回值是value 233 | for k, v := range m1 { 234 | fmt.Printf("%d ----> %s\n", k, v) 235 | //1 ----> abc 236 | //2 ----> def 237 | //3 ----> gh 238 | } 239 | 240 | delete(m1, 2) //删除key值为2的map 241 | 242 | for k, v := range m1 { 243 | fmt.Printf("%d ----> %s\n", k, v) 244 | //1 ----> abc 245 | //3 ----> gh 246 | } 247 | ``` 248 | 249 | ## 函数 250 | 251 | - 函数类型 252 | 253 | > 在Go语言中,函数也是一种数据类型,我们可以通过type来定义它,它的类型就是所有拥有相同的参数,相同的返回值的一种类型。 254 | 255 | ```go 256 | type FuncType func(int, int) int //声明一个函数类型, func后面没有函数名 257 | 258 | //函数中有一个参数类型为函数类型:f FuncType 259 | func Calc(a, b int, f FuncType) (result int) { 260 | result = f(a, b) //通过调用f()实现任务 261 | return 262 | } 263 | 264 | func Add(a, b int) int { 265 | return a + b 266 | } 267 | 268 | func Minus(a, b int) int { 269 | return a - b 270 | } 271 | 272 | func main() { 273 | //函数调用,第三个参数为函数名字,此函数的参数,返回值必须和FuncType类型一致 274 | result := Calc(1, 1, Add) 275 | fmt.Println(result) //2 276 | 277 | var f FuncType = Minus 278 | fmt.Println("result = ", f(10, 2)) //result = 8 279 | } 280 | 281 | ``` 282 | 283 | 284 | 285 | - 匿名函数 286 | 287 | > 在Go里面,函数可以像普通变量一样被传递或使用,Go语言支持随时在代码里定义匿名函数。 288 | 289 | ```go 290 | package main 291 | 292 | func main() { 293 | fn := func() { println("我是一个匿名函数") } 294 | fn() 295 | } 296 | ``` 297 | 298 | 299 | 300 | - 闭包 301 | 302 | > 闭包是由函数及其相关引用环境组合而成的实体(即:闭包=函数+引用环境) 303 | > 304 | > 这就意味着当闭包被调用的时候,不管在程序什么地方调用,闭包能够使用这些常量或者变量。 305 | > 306 | > 它不关心这些捕获了的变量和常量是否已经超出了作用域,所以只有闭包还在使用它,这些变量就还会存在。 307 | 308 | ```go 309 | package main 310 | 311 | import ( 312 | "fmt" 313 | ) 314 | 315 | func a() func() int { 316 | i := 0 317 | b := func() int { 318 | i++ 319 | fmt.Println(i) 320 | return i 321 | } 322 | return b 323 | } 324 | 325 | func main() { 326 | c := a() 327 | c() 328 | c() 329 | c() 330 | } 331 | ``` 332 | 333 | 输出结果 334 | 335 | ```shell 336 | 1 337 | 2 338 | 3 339 | ``` 340 | 341 | - defer延迟调用 342 | 343 | > 关键字 defer ⽤于延迟一个函数的执行 344 | > 345 | > defer语句经常被用于处理成对的操作,如打开、关闭、连接、断开连接、加锁、释放锁。 346 | > 347 | > 如果一个函数中有多个defer语句,它们会以后进先出的顺序执行。 348 | 349 | ```go 350 | func func1(){ 351 | fmt.Println("func1调用") 352 | } 353 | func func2(){ 354 | fmt.Println("func2调用") 355 | } 356 | func func3(){ 357 | fmt.Println("func3调用") 358 | } 359 | func main(){ 360 | defer func1() 361 | defer func2() 362 | defer func3() 363 | fmt.Println("a") 364 | fmt.Println("b") 365 | } 366 | ``` 367 | 368 | 输出 369 | 370 | ```shell 371 | a 372 | b 373 | func3调用 374 | func2调用 375 | func3调用 376 | ``` 377 | 378 | 379 | 380 | ## git版本控制 381 | 382 | - 什么是git 383 | 384 | > Git 是一个开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目。 385 | 386 | - 为什么使用git 387 | 388 | > 说到版本控制,你可能见过以下类似现象 389 | 390 | ``` 391 | 毕业论文_初稿.doc 392 | 毕业论文_修改1.doc 393 | 毕业论文_修改2.doc 394 | 毕业论文_修改3.doc 395 | 毕业论文_完整版1.doc 396 | 毕业论文_完整版2.doc 397 | 毕业论文_完整版3.doc 398 | 毕业论文_最终版1.doc 399 | 毕业论文_最终版2.doc 400 | 毕业论文_死也不改版.doc 401 | ...... 402 | ``` 403 | 404 | > 以上就是使用最原始的方式进行版本控制,但是这种方式有显著缺点: 405 | > 406 | > - 多个文件,保留所有版本时,需要为每个版本保存一个文件。 407 | > - 协同操作,多人协同操作时,需要将文件打包发来发去。 408 | > - 容易丢失,被删除意味着永远失去...(可以选择网盘) 409 | > 410 | > 为了解决以上版本控制存在问题,应运而生了一批版本控制工具:VSS、CVS、SVN、Git等,其中Git应该属于绝对霸主地位。 411 | 412 | 413 | 414 | - 如何使用git 415 | 416 | 网上很多文章讲的很全面,大家可以自行搜索资料,这里推荐几个链接 417 | 418 | [goland配置git](Git版本控制及Goland使用Git教程) 419 | 420 | [git命令行练习游戏](https://learngitbranching.js.org) 421 | 422 | - git常用命令 423 | 424 | - git init 425 | 426 | > Git 使用 git init 命令来初始化一个 Git 仓库,Git 的很多命令都需要在 Git 的仓库中运行,所以 git init 是使用 Git 的第一个命令。 427 | > 428 | > 在执行完成 git init 命令后,Git 仓库会生成一个 .git 目录,该目录包含了资源的所有元数据,其他的项目目录保持不变。 429 | 430 | - git clone 431 | 432 | > 从现有仓库中拷贝项目 433 | > 434 | > `git clone https://github.com/LanshanTeam/Courseware-Backend-Go-2023` 435 | 436 | - git add 437 | 438 | > 添加文件到暂存区 439 | > 440 | > git add . 441 | 442 | - git commit 443 | 444 | > 将暂存区内容添加到仓库中 445 | > 446 | > git commit -m "this is a commit" 447 | 448 | - git push 449 | 450 | > 上传远程代码并合并 451 | 452 | ## 作业 453 | 454 | ### LV1 455 | 456 | 计算器 457 | 458 | 实现一个简单计算器 459 | 460 | 传入两个元素和一个运算符号,符号需要是一个函数类型 461 | 462 | ```go 463 | func Calculator(num1 int, num2 int, CMD func(int, int) int) int { 464 | return CMD(num1, num2) 465 | } 466 | ``` 467 | 468 | 469 | 470 | ### LV2 471 | 472 | 将goland配置git,并在github上共享一个项目 473 | 474 | 可以尝试使用git的命令行 475 | 476 | ### LV3 477 | 478 | [git命令行练习游戏](https://learngitbranching.js.org) 479 | 480 | 尝试完成以下关卡 481 | 482 |  483 | 484 |  485 | 486 | 作业完成后将作业 GitHub 地址发送至 **[xiaote33@qq.com](xiaote33@qq.com)** 487 | 488 | LV2-LV3截图发送即可 489 | 490 | **提交格式(主题):2023xxxx00-刘力延-LV3** (最后LV中写出你完成的最大等级) 491 | 492 | **截止时间**:下一次上课之前 493 | -------------------------------------------------------------------------------- /lesson02/images/img1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson02/images/img1.png -------------------------------------------------------------------------------- /lesson02/images/img2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson02/images/img2.png -------------------------------------------------------------------------------- /lesson02/images/img3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson02/images/img3.png -------------------------------------------------------------------------------- /lesson02/images/img4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson02/images/img4.png -------------------------------------------------------------------------------- /lesson03/第三周课件.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | > 包 4 | > 5 | > 指针 6 | > 7 | > 结构体 8 | > 9 | > 接口 10 | 11 | 12 | 13 | 14 | 15 | # 包 16 | 17 | ### 包介绍 18 | 19 | Go语言中支持模块化的开发理念,在Go语言中使用`包(package)`来支持代码模块化和代码复用。 20 | 21 | 包的引入使得我们可以去调用自己或者别人的模块代码,方便了我们的开发。 22 | 23 | 24 | 25 | 例如,在之前的课件中,我们引入了 fmt 这个包。这样使得我们可以调用 fmt 包内部的函数和变量。 26 | 27 | ```go 28 | package main 29 | 30 | import "fmt" 31 | 32 | func main(){ 33 | fmt.Println("Hello world!") 34 | } 35 | ``` 36 | 37 | 接下来详细介绍一下 38 | 39 | ### 定义包 40 | 41 | 我们可以根据自己的需要创建自定义包。一个包可以简单理解为一个存放`.go`文件的文件夹。 42 | 43 | 该文件夹下面的所有`.go`文件都要在非注释的第一行添加如下声明,声明该文件归属的包。 44 | 45 | ```go 46 | package packagename 47 | ``` 48 | 49 | 另外需要注意一个文件夹下面直接包含的文件只能归属一个包,同一个包的文件不能在多个文件夹下。 50 | 51 | 包名为`main`的包是应用程序的入口包,这种包编译后会得到一个可执行文件,而编译不包含`main`包的源代码则不会得到可执行文件。 52 | 53 | 54 | 55 | ### 可见性 56 | 57 | 在同一个包内部声明的标识符都位于同一个命名空间下,在不同的包内部声明的标识符就属于不同的命名空间。想要在包的外部使用包内部的标识符就需要添加包名前缀,例如`fmt.Println("Hello world!")`。 58 | 59 | 如果想让一个包中的标识符(如变量、常量、类型、函数等)能被外部的包使用,那么标识符必须是对外可见的(public)。在Go语言中是通过标识符的首字母大/小写来控制标识符的对外可见(public)/不可见(private)的。在一个包内部只有首字母大写的标识符才是对外可见的。 60 | 61 | 例如我们定义一个名为`demo`的包,在其中定义了若干标识符。在另外一个包中并不是所有的标识符都能通过`demo.`前缀访问到,因为只有那些首字母是大写的标识符才是对外可见的。 62 | 63 | ```go 64 | var Name string // 可在包外访问的方法 65 | var class string // 仅限包内访问的字段 66 | ``` 67 | 68 | 69 | 70 | ### 包的引入 71 | 72 | 要在当前包中使用另外一个包的内容就需要使用`import`关键字引入这个包,并且import语句通常放在文件的开头,`package`声明语句的下方。完整的引入声明语句格式如下: 73 | 74 | ```go 75 | import importname "path/to/package" 76 | ``` 77 | 78 | 其中: 79 | 80 | - importname:引入的包名,通常都省略。默认值为引入包的包名。 81 | - path/to/package:引入包的路径名称,必须使用双引号包裹起来。 82 | - *`Go语言中禁止循环导入包`* 这个一定要注意,很有可能因为这个要重新架构 83 | 84 | 一个Go源码文件中可以同时引入多个包,例如: 85 | 86 | ```go 87 | import "fmt" 88 | import "net/http" 89 | import "os" 90 | ``` 91 | 92 | 当然可以使用批量引入的方式。 93 | 94 | ```go 95 | import ( 96 | "fmt" 97 | "net/http" 98 | "os" 99 | ) 100 | ``` 101 | 102 | 103 | 104 | 如果引入一个包的时候为其设置了一个特殊`_`作为包名,那么这个包的引入方式就称为匿名引入。 105 | 106 | 一个包被匿名引入的目的主要是为了加载这个包,从而使得这个包中的资源得以初始化。 被匿名引入的包中的`init`函数将被执行并且仅执行一遍。 107 | 108 | ```go 109 | import _ "github.com/go-sql-driver/mysql" 110 | ``` 111 | 112 | 匿名引入的包与其他方式导入的包一样都会被编译到可执行文件中。 113 | 114 | 115 | 116 | 117 | 118 | # Go语言中的指针 119 | 120 | 任何程序数据载入内存后,在内存都有他们的地址,这就是指针。而为了保存一个数据在内存中的地址,我们就需要指针变量。 121 | 122 | `*Go语言中的指针不能进行偏移和运算*`,因此Go语言中的指针操作非常简单,我们只需要记住两个符号:`&`(取地址)和`*`(根据地址取值)。 123 | 124 | 125 | 126 | ## 指针地址和指针类型 127 | 128 | 每个变量在运行时都拥有一个地址,这个地址代表变量在内存中的位置。Go语言中使用`&`字符放在变量前面对变量进行“取地址”操作。 Go语言中的值类型(int、float、bool、string、array、struct)都有对应的指针类型,如:`*int`、`*int64`、`*string`等。 129 | 130 | 取变量指针的语法如下: 131 | 132 | ```go 133 | ptr := &v // v的类型为T 134 | ``` 135 | 136 | 其中: 137 | 138 | - v:代表被取地址的变量,类型为`T` 139 | - ptr:用于接收地址的变量,ptr的类型就为`*T`,称做T的指针类型。*代表指针。 140 | 141 | 举个例子: 142 | 143 | ```go 144 | func main() { 145 | a := 10 146 | b := &a 147 | fmt.Printf("a:%d ptr:%p\n", a, &a) // a:10 ptr:0xc00001a078 148 | fmt.Printf("b:%p type:%T\n", b, b) // b:0xc00001a078 type:*int 149 | fmt.Println(&b) // 0xc00000e018 150 | } 151 | ``` 152 | 153 | 我们来看一下`b := &a`的图示: 154 | 155 | ## 指针取值 156 | 157 | 在对普通变量使用&操作符取地址后会获得这个变量的指针,然后可以对指针使用*操作,也就是指针取值,代码如下。 158 | 159 | ```go 160 | func main() { 161 | //指针取值 162 | a := 10 163 | b := &a // 取变量a的地址,将指针保存到b中 164 | fmt.Printf("type of b:%T\n", b) 165 | c := *b // 指针取值(根据指针去内存取值) 166 | fmt.Printf("type of c:%T\n", c) 167 | fmt.Printf("value of c:%v\n", c) 168 | } 169 | ``` 170 | 171 | 输出如下: 172 | 173 | ```go 174 | type of b:*int 175 | type of c:int 176 | value of c:10 177 | ``` 178 | 179 | **总结:** 取地址操作符`&`和取值操作符`*`是一对互补操作符,`&`取出地址,`*`根据地址取出地址指向的值。 180 | 181 | 变量、指针地址、指针变量、取地址、取值的相互关系和特性如下: 182 | 183 | - 对变量进行取地址(&)操作,可以获得这个变量的指针变量。 184 | - 指针变量的值是指针地址。 185 | - *`对指针变量进行取值(*)操作,可以获得指针变量指向的原变量的值`*。 186 | 187 | **指针传值示例:** 188 | 189 | ```go 190 | func modify1(x int) { 191 | x = 100 192 | } 193 | 194 | func modify2(x *int) { 195 | *x = 100 196 | } 197 | 198 | func main() { 199 | a := 10 200 | modify1(a) 201 | fmt.Println(a) // 10 202 | modify2(&a) 203 | fmt.Printl 204 | n(a) // 100 205 | } 206 | ``` 207 | 208 | 209 | 210 | # 结构体和接口 211 | 212 | Go语言中没有“类”的概念,也不支持“类”的继承等面向对象的概念。 213 | 214 | Go语言中通过结构体的内嵌再配合接口比面向对象具有更高的扩展性和灵活性。 215 | 216 | 217 | 218 | ## 类型别名和自定义类型 219 | 220 | ### 自定义类型 221 | 222 | Go语言中可以使用`type`关键字来定义自定义如`string`、`int`、`bool`等的数据类型。 223 | 224 | 自定义类型是定义了一个***全新***的类型。我们可以基于内置的基本类型定义,也可以通过struct定义。例如: 225 | 226 | ```go 227 | //将MyInt定义为int类型 228 | type MyInt int 229 | ``` 230 | 231 | 通过`type`关键字的定义,`MyInt`就是一种新的类型,它具有`int`的特性。 232 | 233 | 234 | 235 | ## 类型别名 236 | 237 | 类型别名规定:本质上是同一个类型。就像一个孩子小时候有小名,这和他的名字都指向同一个人。 238 | 239 | ```go 240 | type 类型的别名 = 类型名 241 | ``` 242 | 243 | ## 类型定义和类型别名的区别 244 | 245 | 类型别名 和原类型是同一种类型。自定义类型是一种全新的类型。 246 | 247 | 类型别名的类型只会在代码中存在,编译完成时并不会存在。 248 | 249 | 250 | 251 | > 类型别名 和 自定义类型 的意义 252 | 253 | 自定义类型 : 举个例子,我们想给 int 类型定义一个方法,但是又不想改变int本身的性质。可以基于内置的`int`类型使用type关键字可以定义新的自定义类型,然后为我们的自定义类型添加方法。 254 | 255 | 类型别名: 想象一下你有一个非常长的类型名字,比如`map[int]string`,如果在代码中反复使用这个类型,那将会变得很啰嗦。 256 | 257 | 但是如果你使用类型别名来代替它,比如`data`,那么你只需使用`Data`这个简短的名字就可以代替长长的类型名字了 258 | 259 | 260 | 261 | # 结构体 262 | 263 | Go语言中的基础数据类型可以表示一些事物的基本属性,但是当我们想表达一个事物的全部或部分属性时,这时候再用单一的基本数据类型明显就无法满足需求了,Go语言提供了一种自定义数据类型,可以封装多个基本数据类型,这种数据类型叫结构体,英文名称`struct`。 264 | 265 | 也就是我们可以通过`struct`来定义自己的类型了。 266 | 267 | 268 | 269 | ## 结构体的定义 270 | 271 | 使用`type`和`struct`关键字来定义结构体,具体代码格式如下: 272 | 273 | ```go 274 | type 类型名 struct { 275 | 字段名 字段类型 276 | 字段名 字段类型 277 | … 278 | } 279 | ``` 280 | 281 | 其中: 282 | 283 | - 类型名:标识自定义结构体的名称,在同一个包内不能重复。 284 | - 字段名:表示结构体字段名。结构体中的字段名必须唯一。 285 | - 字段类型:表示结构体字段的具体类型。 286 | 287 | 举个例子,我们定义一个`Person`(人)结构体,代码如下: 288 | 289 | ```go 290 | type person struct { 291 | name string 292 | city string 293 | age int8 294 | } 295 | ``` 296 | 297 | 这样我们就拥有了一个`person`的自定义类型。 298 | 299 | 我们通过`.`来访问结构体的字段(成员变量),例如`p1.name`和`p1.age`等。 300 | 301 | ```go 302 | var p1 person 303 | p1.name = "zero" 304 | p1.age = "18" 305 | ``` 306 | 307 | 308 | 309 | ## 结构体初始化 310 | 311 | 没有初始化的结构体,其成员变量都是对应其类型的零值。 312 | 313 | ```go 314 | type person struct { // 创建一个结构体类型,像int这种类型一样 315 | name string 316 | city string 317 | age int8 318 | } 319 | 320 | func main() { 321 | var p4 person //将结构体类型实例化,但是没有初始化 322 | fmt.Printf("p4=%#v\n", p4) //p4=main.person{name:"", city:"", age:0} 323 | } 324 | ``` 325 | 326 | ### 使用键值对初始化 327 | 328 | 使用键值对对结构体进行初始化时,键对应结构体的字段,值对应该字段的初始值。 329 | 330 | ```go 331 | p5 := person{ 332 | name: "小王子", 333 | city: "北京", 334 | age: 18, 335 | } 336 | fmt.Printf("p5=%#v\n", p5) //p5=main.person{name:"小王子", city:"北京", age:18} 337 | ``` 338 | 339 | > PS : golang 结构体的初始化有很多方式,课件有参考链接,建议大家下去看看(会用就好) 340 | 341 | 342 | 343 | ## 嵌套结构体 344 | 345 | 一个结构体中可以嵌套包含另一个结构体或结构体指针,就像下面的示例代码那样。 346 | 347 | ```go 348 | //Address 地址结构体 349 | type Address struct { 350 | Province string 351 | City string 352 | } 353 | 354 | //User 用户结构体 355 | type User struct { 356 | Name string 357 | Gender string 358 | Address Address 359 | } 360 | 361 | func main() { 362 | user1 := User{ 363 | Name: "小王子", 364 | Gender: "男", 365 | Address: Address{ 366 | Province: "山东", 367 | City: "威海", 368 | }, 369 | } 370 | fmt.Printf("user1=%#v\n", user1)//user1=main.User{Name:"小王子", Gender:"男", Address:main.Address{Province:"山东", City:"威海"}} 371 | } 372 | ``` 373 | 374 | 375 | 376 | ## 方法和接收者 377 | 378 | Go语言中的`方法(Method)`是一种作用于特定类型变量的函数。这种特定类型变量叫做`接收者(Receiver)`。 379 | 380 | 只有特定的接收者变量才可以调用对应的方法。 381 | 382 | 方法的定义格式如下: 383 | 384 | ```go 385 | func (接收者变量 接收者类型) 方法名(参数列表) (返回参数) { 386 | 函数体 387 | } 388 | ``` 389 | 390 | 其中, 391 | 392 | - 接收者变量:接收者中的参数变量名在命名时,官方建议使用接收者类型名称首字母的小写。例如,`Person`类型的接收者变量应该命名为 `p`。 393 | - 接收者类型:接收者类型和参数类似,可以是`指针类型`和`非指针类型`。 394 | - 方法名、参数列表、返回参数:具体格式与函数定义相同。 395 | 396 | 举个例子: 397 | 398 | ```go 399 | // 结构体 400 | type Person struct { 401 | name string 402 | age int 403 | } 404 | 405 | //NewPerson 构造函数 406 | func NewPerson(name string, age int) *Person { 407 | return &Person{ 408 | name: name, 409 | age: age, 410 | } 411 | } 412 | 413 | // 为 person这个类型创建一个学习的方法 414 | func (p Person) Study(){ 415 | fmt.Print("我要卷四各位,或者被各位卷四") 416 | } 417 | ``` 418 | 419 | 420 | 421 | ### 指针类型的接收者 422 | 423 | 指针类型的接收者由一个结构体的指针组成,由于指针的特性,调用方法时修改接收者指针的任意成员变量,在方法结束后,修改都是有效的。 例如我们为`Person`添加一个`SetAge`方法,来修改实例变量的年龄。 424 | 425 | ```go 426 | // SetAge 设置p的年龄 427 | // 使用指针接收者 428 | func (p *Person) SetAge(newAge int) { 429 | p.age = newAge 430 | } 431 | ``` 432 | 433 | 调用该方法: 434 | 435 | ```go 436 | func main() { 437 | p1 := NewPerson("小王子", 25) 438 | fmt.Println(p1.age) // 25 439 | p1.SetAge(30) 440 | fmt.Println(p1.age) // 30 441 | } 442 | ``` 443 | 444 | ### 值类型的接收者 445 | 446 | 当方法作用于值类型接收者时,Go语言会在代码运行时将接收者的值复制一份。在值类型接收者的方法中可以获取接收者的成员值,但修改操作只是针对副本,无法修改接收者变量本身。 447 | 448 | ```go 449 | // SetAge2 设置p的年龄 450 | // 使用值接收者 451 | func (p Person) SetAge2(newAge int8) { 452 | p.age = newAge 453 | } 454 | 455 | func main() { 456 | p1 := NewPerson("小王子", 25) 457 | p1.Dream() 458 | fmt.Println(p1.age) // 25 459 | p1.SetAge2(30) // (*p1).SetAge2(30) 460 | fmt.Println(p1.age) // 25 461 | } 462 | ``` 463 | 464 | ### 什么时候应该使用指针类型接收者 465 | 466 | 1. 需要修改接收者中的值 467 | 468 | 2. 接收者是拷贝代价比较大的大对象 469 | 470 | 3. 保证一致性,如果有某个方法使用了指针接收者,那么其他的方法也应该使用指针接收者。 471 | 472 | 473 | 474 | **注意事项:** 非本地类型不能定义方法,也就是说我们不能给别的包的类型定义方法。 475 | 476 | 477 | 478 | # 接口 479 | 480 | > 接口是一种类型 481 | 482 | 接口是一种由程序员来定义的类型,一个接口类型就是一组方法的集合,它规定了需要实现的所有方法。 483 | 484 | 相较于使用结构体类型,当我们使用接口类型说明相比于它是什么更关心**它能做什么**,而不是它是什么。 485 | 486 | ### 接口的定义 487 | 488 | 每个接口类型由任意个方法签名组成,接口的定义格式如下: 489 | 490 | ```go 491 | type 接口类型名 interface{ 492 | 方法名1( 参数列表1 ) 返回值列表1 493 | 方法名2( 参数列表2 ) 返回值列表2 494 | … 495 | } 496 | ``` 497 | 498 | 其中: 499 | 500 | - 接口类型名:Go语言的接口在命名时,一般会在单词后面添加`er`,如有写操作的接口叫`Writer`,有关闭操作的接口叫`closer`等。接口名最好要能突出该接口的类型含义。 501 | - 方法名:当方法名首字母是大写且这个接口类型名首字母也是大写时,这个方法可以被接口所在的包(package)之外的代码访问。 502 | - 参数列表、返回值列表:参数列表和返回值列表中的参数变量名可以省略。 503 | 504 | 举个例子,定义一个包含`Write`方法的`Writer`接口。 505 | 506 | ```go 507 | type Writer interface{ 508 | Write([]byte) error 509 | } 510 | ``` 511 | 512 | 当你看到一个`Writer`接口类型的值时,你不知道它是什么,唯一知道的就是可以通过调用它的`Write`方法来做一些事情。 513 | 514 | 515 | 516 | ### 实现接口的条件 517 | 518 | 接口就是规定了一个**需要实现的方法列表**,在 Go 语言中一个类型只要实现了接口中规定的所有方法,那么我们就称它实现了这个接口。 519 | 520 | 我们定义的`Singer`接口类型,它包含一个`Sing`方法。 521 | 522 | ```go 523 | // Singer 接口 可以称之为 “会唱歌的东西” 524 | type Singer interface { 525 | Sing() 526 | } 527 | ``` 528 | 529 | 我们有一个`Bird`结构体类型如下。 530 | 531 | ```go 532 | type Bird struct {} 533 | ``` 534 | 535 | 因为`Singer`接口只包含一个`Sing`方法,所以只需要给`Bird`结构体添加一个`Sing`方法就可以满足`Singer`接口的要求。 536 | 537 | ```go 538 | // Sing Bird类型的Sing方法 539 | func (b Bird) Sing() { 540 | fmt.Println("汪汪汪") 541 | } 542 | ``` 543 | 544 | 这样就称为`Bird`实现了`Singer`接口。bird 就属于singer 类型了。 545 | 546 | 547 | 548 | ### ***接口的意义*** 549 | 550 | 551 | 552 | 1️⃣ 实现多态性(Polymorphism):通过接口,可以实现多个不同类型的对象以相同的方式进行操作,这增强了代码的灵活性和可复用性。 553 | 554 | 2️⃣ 解耦合(Decoupling):接口使得模块之间的依赖关系更松散,模块只需要关注接口定义的方法,而不需要知道具体的实现细节。这有助于降低代码的耦合度,增加代码的可维护性和可测试性。 555 | 556 | 3️⃣ 扩展性(Extensibility):通过接口定义通用的行为,可以方便地对系统进行扩展和修改,而不需要改动已有的代码。当需要添加新的功能时,只需要实现接口定义的方法即可。 557 | 558 | 4️⃣ 接口断言(Interface Assertion):使用接口断言可以在运行时检查一个对象是否实现了某个接口,并根据情况进行处理。这样可以进行更灵活的类型转换和错误处理。 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | ### 小练习 567 | 568 | 你要设计一个电子商务平台,该平台有多种类型的商品,例如电子产品、家居用品和服装等。你需要设计以下结构体和功能: 569 | 570 | 1. 实现商品结构体:包含商品的名称、价格和库存数量等信息。 571 | 572 | 2. 实现接口:定义商品的库存管理功能,包括检查库存数量、更新库存数量和打印库存信息等。 573 | 574 | 3. 实现电子产品结构体:继承自商品结构体,同时具有电子产品特有的属性,例如品牌和型号。 575 | 576 | 4. 实现接口:定义电子产品结构体的库存管理功能(同上),以及打印品牌型号信息的功能。 577 | 578 | 579 | 580 | 完成后提交到邮箱saleroa@qq.com 581 | 582 | 有什么问题也可以发邮箱问哦 583 | 584 | 585 | 586 | 587 | 588 | # 参考链接 589 | 590 | 591 | 592 | [Go语言基础之包 | 李文周的博客 (liwenzhou.com)](https://www.liwenzhou.com/posts/Go/package/) 593 | 594 | [Go语言基础之指针 | 李文周的博客 (liwenzhou.com)](https://www.liwenzhou.com/posts/Go/pointer/) 595 | 596 | [Go语言基础之结构体 | 李文周的博客 (liwenzhou.com)](https://www.liwenzhou.com/posts/Go/struct/) 597 | 598 | [Go语言基础之接口 | 李文周的博客 (liwenzhou.com)](https://www.liwenzhou.com/posts/Go/interface/) 599 | 600 | 601 | 602 | -------------------------------------------------------------------------------- /lesson04/img/img01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson04/img/img01.png -------------------------------------------------------------------------------- /lesson04/img/img02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson04/img/img02.png -------------------------------------------------------------------------------- /lesson04/img/img03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson04/img/img03.png -------------------------------------------------------------------------------- /lesson04/img/img04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson04/img/img04.png -------------------------------------------------------------------------------- /lesson05/images/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson05/images/1.png -------------------------------------------------------------------------------- /lesson05/images/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson05/images/10.png -------------------------------------------------------------------------------- /lesson05/images/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson05/images/11.png -------------------------------------------------------------------------------- /lesson05/images/12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson05/images/12.png -------------------------------------------------------------------------------- /lesson05/images/13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson05/images/13.png -------------------------------------------------------------------------------- /lesson05/images/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson05/images/2.png -------------------------------------------------------------------------------- /lesson05/images/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson05/images/3.png -------------------------------------------------------------------------------- /lesson05/images/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson05/images/4.png -------------------------------------------------------------------------------- /lesson05/images/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson05/images/5.png -------------------------------------------------------------------------------- /lesson05/images/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson05/images/6.png -------------------------------------------------------------------------------- /lesson05/images/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson05/images/7.png -------------------------------------------------------------------------------- /lesson05/images/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson05/images/8.png -------------------------------------------------------------------------------- /lesson05/images/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson05/images/9.png -------------------------------------------------------------------------------- /lesson06/clain/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/gin-gonic/gin" 6 | ) 7 | 8 | func main() { 9 | r := gin.Default() 10 | //在这里使用了三个中间件 11 | r.Use(mid1(), mid2(), mid3()) 12 | r.GET("/abc", func(context *gin.Context) { 13 | fmt.Println("1") 14 | }) 15 | _ = r.Run(":8081") 16 | } 17 | 18 | func mid1() gin.HandlerFunc { 19 | return func(c *gin.Context) { 20 | fmt.Println("mid1 before") 21 | //先执行next,后执行after 22 | c.Next() 23 | fmt.Println("mid1 after") 24 | } 25 | } 26 | func mid2() gin.HandlerFunc { 27 | return func(c *gin.Context) { 28 | fmt.Println("mid2 before") 29 | c.Abort() 30 | //c.Next() 31 | fmt.Println("mid2 after") 32 | } 33 | } 34 | 35 | func mid3() gin.HandlerFunc { 36 | return func(c *gin.Context) { 37 | fmt.Println("mid3 before") 38 | c.Next() 39 | fmt.Println("mid3 after") 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lesson06/m1/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/gin-gonic/gin" 4 | 5 | func main() { 6 | r := gin.Default() 7 | r.GET("/ping", func(c *gin.Context) { 8 | c.JSON(200, gin.H{ 9 | "message": "pong", 10 | }) 11 | }) 12 | r.Run(":8080") // 监听并在 0.0.0.0:8080 上启动服务 13 | } 14 | -------------------------------------------------------------------------------- /lesson06/m2/api/middleware/cors.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import "github.com/gin-gonic/gin" 4 | 5 | func CORS() gin.HandlerFunc { 6 | return func(ctx *gin.Context) { 7 | ctx.Writer.Header().Set("Access-Control-Allow-Origin", "*") 8 | ctx.Writer.Header().Set("Access-Control-Allow-Credentials", "true") 9 | ctx.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With, token, x-access-token") 10 | ctx.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT, DELETE") 11 | 12 | if ctx.Request.Method == "OPTIONS" { 13 | ctx.AbortWithStatus(204) 14 | return 15 | } 16 | ctx.Next() 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lesson06/m2/api/middleware/jwt.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "errors" 5 | "github.com/dgrijalva/jwt-go" 6 | "net/http" 7 | "strings" 8 | 9 | "Courseware-Backend-Go-2023/lesson6/m2/model" 10 | "github.com/gin-gonic/gin" 11 | ) 12 | 13 | var Secret = []byte("Lanshan") 14 | 15 | // JWTAuthMiddleware 基于JWT的认证中间件 16 | func JWTAuthMiddleware() func(c *gin.Context) { 17 | return func(c *gin.Context) { 18 | // 客户端携带Token有三种方式 1.放在请求头 2.放在请求体 3.放在URI 19 | // 这里假设Token放在Header的Authorization中,并使用Bearer开头 20 | // 这里的具体实现方式要依据你的实际业务情况决定 21 | authHeader := c.Request.Header.Get("Authorization") 22 | if authHeader == "" { 23 | c.JSON(http.StatusOK, gin.H{ 24 | "code": 2003, 25 | "msg": "请求头中auth为空", 26 | }) 27 | c.Abort() 28 | return 29 | } 30 | // 按空格分割 31 | parts := strings.SplitN(authHeader, " ", 2) 32 | if !(len(parts) == 2 && parts[0] == "Bearer") { 33 | c.JSON(http.StatusOK, gin.H{ 34 | "code": 2004, 35 | "msg": "请求头中auth格式有误", 36 | }) 37 | c.Abort() 38 | return 39 | } 40 | // parts[1]是获取到的tokenString,我们使用之前定义好的解析JWT的函数来解析它 41 | mc, err := ParseToken(parts[1]) 42 | if err != nil { 43 | c.JSON(http.StatusOK, gin.H{ 44 | "code": 2005, 45 | "msg": "无效的Token", 46 | }) 47 | c.Abort() 48 | return 49 | } 50 | // 将当前请求的username信息保存到请求的上下文c上 51 | c.Set("username", mc.Username) 52 | c.Next() // 后续的处理函数可以用过c.Get("username")来获取当前请求的用户信息 53 | } 54 | } 55 | 56 | // ParseToken 解析JWT 57 | func ParseToken(tokenString string) (*model.MyClaims, error) { 58 | // 解析token 59 | token, err := jwt.ParseWithClaims(tokenString, &model.MyClaims{}, func(token *jwt.Token) (i interface{}, err error) { 60 | return Secret, nil 61 | }) 62 | if err != nil { 63 | return nil, err 64 | } 65 | if claims, ok := token.Claims.(*model.MyClaims); ok && token.Valid { // 校验token 66 | return claims, nil 67 | } 68 | return nil, errors.New("invalid token") 69 | } 70 | -------------------------------------------------------------------------------- /lesson06/m2/api/router.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "Courseware-Backend-Go-2023/lesson6/m2/api/middleware" 5 | "github.com/gin-gonic/gin" 6 | ) 7 | 8 | func InitRouter() { 9 | r := gin.Default() 10 | r.Use(middleware.CORS()) 11 | 12 | r.POST("/register", register) // 注册 13 | r.POST("/login", login) // 登录 14 | 15 | UserRouter := r.Group("/user") 16 | { 17 | UserRouter.Use(middleware.JWTAuthMiddleware()) 18 | UserRouter.GET("/get", getUsernameFromToken) 19 | } 20 | 21 | r.Run(":8088") // 跑在 8088 端口上 22 | } 23 | -------------------------------------------------------------------------------- /lesson06/m2/api/user.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "Class/2/api/middleware" 5 | "Class/2/dao" 6 | "Class/2/model" 7 | "Class/2/utils" 8 | "fmt" 9 | "github.com/dgrijalva/jwt-go" 10 | "github.com/gin-gonic/gin" 11 | "time" 12 | ) 13 | 14 | func register(c *gin.Context) { 15 | form := model.User{} 16 | if err := c.ShouldBind(&form); err != nil { 17 | fmt.Println(err) 18 | utils.RespSuccess(c, "verification failed") 19 | return 20 | } 21 | // 传入用户名和密码 22 | username := c.PostForm("username") 23 | password := c.PostForm("password") 24 | 25 | // 验证用户名是否重复 26 | flag := dao.SelectUser(username) 27 | fmt.Println(flag) 28 | if flag { 29 | // 以 JSON 格式返回信息 30 | utils.RespFail(c, "user already exists") 31 | return 32 | } 33 | 34 | dao.AddUser(username, password) 35 | // 以 JSON 格式返回信息 36 | utils.RespSuccess(c, "add user successful") 37 | } 38 | 39 | func login(c *gin.Context) { 40 | if err := c.ShouldBind(&model.User{}); err != nil { 41 | utils.RespFail(c, "verification failed") 42 | return 43 | } 44 | // 传入用户名和密码 45 | username := c.PostForm("username") 46 | password := c.PostForm("password") 47 | 48 | // 验证用户名是否存在 49 | flag := dao.SelectUser(username) 50 | // 不存在则退出 51 | if !flag { 52 | // 以 JSON 格式返回信息 53 | utils.RespFail(c, "user doesn't exists") 54 | return 55 | } 56 | 57 | // 查找正确的密码 58 | selectPassword := dao.SelectPasswordFromUsername(username) 59 | // 若不正确则传出错误 60 | if selectPassword != password { 61 | // 以 JSON 格式返回信息 62 | utils.RespFail(c, "wrong password") 63 | return 64 | } 65 | 66 | // 正确则登录成功 67 | // 创建一个我们自己的声明 68 | claim := model.MyClaims{ 69 | Username: username, // 自定义字段 70 | StandardClaims: jwt.StandardClaims{ 71 | ExpiresAt: time.Now().Add(time.Hour * 2).Unix(), // 过期时间 72 | Issuer: "Yxh", // 签发人 73 | }, 74 | } 75 | // 使用指定的签名方法创建签名对象 76 | token := jwt.NewWithClaims(jwt.SigningMethodHS256, claim) 77 | // 使用指定的secret签名并获得完整的编码后的字符串token 78 | tokenString, _ := token.SignedString(middleware.Secret) 79 | utils.RespSuccess(c, tokenString) 80 | } 81 | 82 | func getUsernameFromToken(c *gin.Context) { 83 | username, _ := c.Get("username") 84 | utils.RespSuccess(c, username.(string)) 85 | } 86 | -------------------------------------------------------------------------------- /lesson06/m2/dao/user.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | var database = map[string]string{ 4 | "yxh": "123456", 5 | "wx": "654321", 6 | } 7 | 8 | func AddUser(username, password string) { 9 | database[username] = password 10 | } 11 | 12 | func SelectUser(username string) bool { 13 | if database[username] == "" { 14 | return false 15 | } 16 | return true 17 | } 18 | 19 | func SelectPasswordFromUsername(username string) string { 20 | return database[username] 21 | } 22 | -------------------------------------------------------------------------------- /lesson06/m2/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "Courseware-Backend-Go-2023/lesson6/m2/api" 4 | 5 | func main() { 6 | api.InitRouter() 7 | 8 | } 9 | -------------------------------------------------------------------------------- /lesson06/m2/model/user.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import "github.com/dgrijalva/jwt-go" 4 | 5 | type User struct { 6 | Username string `form:"username" json:"username" binding:"required"` 7 | Password string `form:"password" json:"password" binding:"required"` 8 | } 9 | 10 | type MyClaims struct { 11 | Username string `json:"username"` 12 | jwt.StandardClaims 13 | } 14 | -------------------------------------------------------------------------------- /lesson06/m2/utils/response.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "net/http" 6 | ) 7 | 8 | func RespSuccess(c *gin.Context, message string) { 9 | c.JSON(http.StatusOK, gin.H{ 10 | "status": 200, 11 | "message": message, 12 | }) 13 | } 14 | 15 | func RespFail(c *gin.Context, message string) { 16 | c.JSON(http.StatusInternalServerError, gin.H{ 17 | "status": 500, 18 | "message": message, 19 | }) 20 | } 21 | -------------------------------------------------------------------------------- /lesson07/lesson7.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # MySQL 4 | 5 |  6 | 7 | ## 概念篇 8 | 9 | ### 什么是数据库? 10 | 11 | 数据库(Database)是指存储数据的容器,也被称为数据存储库(Data Store)。数据库能够存储大量结构化和非结构化的数据,包括文本、数字、图像、音频等各种类型的数据。 12 | 13 | 根据数据存储方式和结构的不同,数据库可以分为关系型数据库(如MySQL、Oracle、SQL Server、PostgreSQL)和非关系型数据库(如MongoDB、Cassandra、Redis、Elasticsearch)。 14 | 15 | ### MySQL介绍 16 | 17 | > MySQL是一个**关系型数据库**,一种开源关系数据库管理系统(RDBMS),它使用最常用的数据库管理语言-结构化查询语言(SQL)进行数据库管理 18 | 19 | 关系型数据库的数据都以**数据表**的形式进行存储和管理。 20 | 21 | 表是一种二维行列表格,关系型数据库中的数据通常以表的形式存储,如图所示 22 | 23 |  24 | 25 | - `列(column)` - 表中的一个字段 ,如图中`patient_id` 。所有表都是由一个或多个列组成的 26 | 27 | - `行(row)` - 表中的一个记录,比如第一行,这条记录,记录下了小红的信息(病人号、姓名、年龄、身高体重等等) 28 | 29 | - `主键(primary key)` - 一列(或一组列),其值能够唯一标识表中每一行。 30 | 31 | > 比如图2中每个patient_id,都标识了一个**独一无二**的病人 32 | > 33 | > 又比如b站的uid,也标识了一个**独一无二**的用户 34 | 35 | #### SQL 36 | 37 | > SQL 是一种**操作数据库**的语言,包括创建数据库、删除数据库、查询记录、修改记录、添加字段等。 38 | > 39 | > SQL 是关系型数据库的标准语言,所有的关系型数据库管理系统(RDBMS)都将 SQL 作为其标准处理语言。 40 | 41 | SQL有以下用途: 42 | 43 | - 允许用户访问关系型数据库系统中的数据;(**查数据**) 44 | 45 | - 允许用户描述数据;(**自定义要查哪些数据**) 46 | 47 | - 允许用户定义数据库中的数据,并处理该数据;(**建表**) 48 | 49 | - 允许将 SQL 模块、库或者预处理器嵌入到其它编程语言中;(**在Go中使用SQL**) 50 | 51 | - 允许用户创建和删除数据库、表、数据项(记录); 52 | 53 | - 允许用户在数据库中创建视图、存储过程、函数; 54 | 55 | - 允许用户设置对表、存储过程和视图的权限; 56 | 57 | **SQL命令分为三类** 58 | 59 | 1.DDL - Data Definition Language,数据定义语言 60 | 61 | > 对数据的结构和形式进行定义,一般用于数据库和表的创建、删除、修改等。 62 | 63 | | 命令 | 说明 | 64 | | ------ | -------------------------------------------------- | 65 | | CREATE | 用于在数据库中创建一个新表、一个视图或者其它对象。 | 66 | | ALTER | 用于修改现有的数据库,比如表、记录。 | 67 | | DROP | 用于删除整个表、视图或者数据库中的其它对象 | 68 | 69 | 2.DML - Data Manipulation Language,数据处理语言 70 | 71 | > 对数据库中的数据进行处理,一般用于数据项(记录)的插入、删除、修改和查询。 72 | 73 | | 命令 | 说明 | 74 | | ------ | ------------------------------------ | 75 | | SELECT | 用于从一个或者多个表中检索某些记录。 | 76 | | INSERT | 插入一条记录。 | 77 | | UPDATE | 修改记录。 | 78 | | DELETE | 删除记录。 | 79 | 80 | 3.DCL - Data Control Language,数据控制语言 81 | 82 | | 命令 | 说明 | 83 | | --------------- | ------------------------------------------ | 84 | | COMMIT | 提交 | 85 | | SAVEPOINT | 保存点 | 86 | | ROLLBACK | 回滚 | 87 | | SET TRANSACTION | 设置当前事务的特性,它对后面的事务没有影响 | 88 | 89 | #### 数据类型 90 | 91 | MySQL 支持多种数据类型,大致可以分为三类:数值、日期/时间和字符串(字符)类型。 92 | 93 | ##### 数值类型 94 | 95 | | 类型 | 字节大小 | 96 | | --------------- | --------------------------------------- | 97 | | TINYINT | 1 | 98 | | SMALLINT | 2 | 99 | | MEDIUMINT | 3 | 100 | | INT(INTEGER) | 4 | 101 | | BIGINT | 8 | 102 | | FLOAT | 4 | 103 | | DOUBLE | 8 | 104 | | DECIMAL(小数) | 对DECIMAL(M,D)中 M>D 则为M+2,否则为D+2 | 105 | 106 | ##### 日期和时间类型 107 | 108 | | **类型** | **字节大小** | **格式** | 用途 | 109 | | --------- | ------------ | ------------------- | ------------------------ | 110 | | DATE | 3 | YYYY-MM-DD | 日期值 | 111 | | TIME | 3 | HH:MM:SS | 时间值或持续时间 | 112 | | YEAR | 1 | YYYY | 年份值 | 113 | | DATETIME | 8 | YYYY-MM-DD hh:mm:ss | 混合日期和时间值 | 114 | | TIMESTAMP | 4 | YYYY-MM-DD hh:mm:ss | 混合日期和时间值,时间戳 | 115 | 116 | ##### 字符串类型 117 | 118 | | 类型 | 字节大小 | 用途 | 119 | | ----------- | ------------ | ---------------------------- | 120 | | **CHAR** | 0-255 | 定长字符串 | 121 | | **VARCHAR** | 0-65535 | 变长字符串 | 122 | | TINYBLOB | 0-255 | <=255个字符的二进制字符串 | 123 | | TINYTEXT | 0-255 | 短文本字符串 | 124 | | BLOB | 0-65535 | 二进制形式的长文本数据 | 125 | | TEXT | 0-65535 | 长文本数据 | 126 | | MEDIUMBLOB | 0-16777215 | 二进制形式的中等长度文本数据 | 127 | | MEDIUMTEXT | 0-16777215 | 中等长度文本数据 | 128 | | LONGBLOB | 0-4294967295 | 二进制形式的极大文本数据 | 129 | | LONGTEXT | 0-4294967295 | 极大文本数据 | 130 | 131 | ## 实践篇 132 | 133 | **在开始实践前 请先安装好MySQL和配置好环境变量。** 134 | 135 | ### SQL语法 136 | 137 | 使用MySQL前当然要学会使用SQL语句。 138 | 139 | 我们先来了解一下SQL的基本规则 140 | 141 | 1. **SQL 语句要以分号`;`结尾**,在 RDBMS (关系型数据库)当中,SQL 语句是逐条执行的,一条 SQL 语句代表着数据库的一个操作 142 | 2. **SQL 语句不区分大小写**,例如,不管写成 SELECT 还是 select,解释都是一样的。表名和列名也是如此,但是,**插入到表中的数据是区分大小写的** 143 | 3. **SQL 语句的单词之间必须使用半角空格(英文空格)或换行符来进行分隔** 144 | 145 | **登录MySQL** 146 | 147 | windows系统打开cmd或windows powershell;mac和linux打开终端 148 | 149 | 输入 150 | 151 | ``` 152 | mysql -u root -p 153 | ``` 154 | 155 | root是你的用户名 156 | 157 | 然后根据提示输入密码来启动MySQL 158 | 159 | 下面来一些 SQL 语句的示例 160 | 161 | ```sql 162 | create database student; 163 | //创建数据库 164 | 165 | drop database school; 166 | //删除数据库 167 | 168 | show databases; 169 | //查看所有数据库 170 | 171 | show databases like 'school'; 172 | //查看名为 "school" 的数据库 173 | 174 | show databases like '%sch%'; 175 | //查看名字中包含sch的数据库(%表示任意个字符) 176 | 177 | show databases like 'sch%'; 178 | //查看以sch开头的数据库 179 | 180 | use school 181 | //进入数据库"school" 182 | 183 | CREATE TABLE `student` ( 184 | `id` BIGINT(20) NOT NULL AUTO_INCREMENT, 185 | `name` VARCHAR(20) DEFAULT '1', 186 | `sex` VARCHAR(8) DEFAULT '', 187 | `age` INT(11) , 188 | PRIMARY KEY(`id`) 189 | )ENGINE=InnoDB AUTO_INCREMENT=1 CHARSET=utf8mb4; 190 | //创建表"student",ENGINE=InnoDB:指定 MySQL引擎为InnoDB,AUTO_INCREMENT=1:自增的字段每次自增1,CHARSET=utf8mb4:指定编码为utf8base64以支持中文 191 | 192 | drop table student; 193 | //删除表"student" 194 | ``` 195 | 196 | 接下来我们以创建好的 student 表为例,对数据进行操作 197 | 198 | **插入 Insert** 199 | 200 | > 由于id是自增,所以添加的时候无需插入id 201 | 202 | ``` 203 | insert into 表名(列1,列2,列3,列4,...) values(值,值,值) 204 | ``` 205 | 206 | ```sql 207 | insert into student(name,sex,age) values('小明','男',18); 208 | ``` 209 | 210 | 当字段允许空时或者设置了默认值时,也可以不插入这个字段,字段会默认为空或者默认值 211 | 212 | ```sql 213 | insert into student(name,sex) values('小红','女'); 214 | ``` 215 | 216 | **查询 Select** 217 | 218 | ``` 219 | select 列1,列2 from 表名 where ...; 220 | ``` 221 | 222 | 查询所有学生 223 | 224 | ```sql 225 | select *from student; 226 | ``` 227 | 228 | 查询所有学生的姓名与年龄 229 | 230 | ```sql 231 | select name,age from student; 232 | ``` 233 | 234 | 查询男学生 235 | 236 | ```sql 237 | select name,age from student where sex='男'; 238 | ``` 239 | 240 | 如果想改变输出时的字段名 可以使用**AS** 241 | 242 | ```sql 243 | select name as '姓名' from student; 244 | ``` 245 | 246 | **子查询** 247 | 248 | 顾名思义,子查询嵌套在另一个查询语句里 249 | 250 | ```sql 251 | select name,age from student where name in( 252 | select name from student where sex='男' 253 | )order by id desc; 254 | ``` 255 | 256 | 我们将这条语句拆解一下 257 | 258 | 最外层: 259 | 260 | ```sql 261 | select name,age from student where name in(..)order by id desc; 262 | ``` 263 | 264 | 里层: 265 | 266 | ```sql 267 | select name from student where sex='男'; 268 | ``` 269 | 270 | 外层在里层结果的基础上再进行查询 271 | 272 | 最后的: 273 | 274 | ```sql 275 | order by id desc; 276 | ``` 277 | 278 | 意思是结果按id排序,降序(desc) 279 | 280 | 升序是`asc` 281 | 282 | **更新 Update** 283 | 284 | ``` 285 | update <表名> set 列1=值1,列2=值2,.... where... 286 | ``` 287 | 288 | ```sql 289 | update student set age=19,sex='女' where id=1; 290 | ``` 291 | 292 | 这里用`where`来限定要更新的记录,因为update会**更新所有**符合限定条件的记录,如果不限定,会更新所有记录 293 | 294 | 条件之间可以用`and`、`or`连接 295 | 296 | ```sql 297 | update student set age=19,sex='女' where id>=1 and name='小明'; 298 | ``` 299 | 300 | **删除 Delete** 301 | 302 | ```sql 303 | delete from student where id=1; 304 | ``` 305 | 306 | **修改数据表** 307 | 308 | 修改字段 309 | 310 | ``` 311 | alter table <表名> change <旧字段名> <新字段名> <新数据类型>; 312 | ``` 313 | 314 | ``` 315 | alter table student change name 姓名 varchar(32); 316 | ``` 317 | 318 | 删除字段 319 | 320 | ``` 321 | alter table student drop 姓名; 322 | ``` 323 | 324 | 添加字段 325 | 326 | > 需要注意的是,添加的字段会默认在表最后一列 327 | 328 | ``` 329 | alter table <表名> add <新字段名><数据类型>[约束条件]; 330 | ``` 331 | 332 | ``` 333 | alter table student add name varchar(12) default '李华' not null; 334 | ``` 335 | 336 | 在开头添加字段 337 | 338 | > 末尾加上first即可 339 | 340 | ``` 341 | alter table student add name varchar(12) default '李华' not null first; 342 | ``` 343 | 344 | 在中间添加字段 345 | 346 | > 末尾加after接字段名即可 347 | 348 | ``` 349 | alter table student add name varchar(12) default '李华' not null after id; 350 | ``` 351 | 352 | ### Go操作MySQL 353 | 354 | 上述内容是直接使用SQL语句操作MySQL,下面我们学习使用Golang来操作数据库。 355 | 356 | Go官方为我们提供了 **database/sql** 包保证SQL或类SQL数据库的泛用接口,**sqlx** 是基于标准库的扩展,在开发中 **sqlx** 包使用更加广泛,下面我们以使用 **sqlx** 包为例。 357 | 358 | 首先下载 MySQL 驱动 359 | 360 | ```go 361 | go get -u github.com/go-sql-driver/mysql 362 | ``` 363 | 364 | 然后安装 sqlx 365 | 366 | ```go 367 | go get github.com/jmoiron/sqlx 368 | ``` 369 | 370 | #### 连接数据库 371 | 372 | ```go 373 | package main 374 | 375 | import ( 376 | _ "github.com/go-sql-driver/mysql" //要导入相应驱动包,否则会报错 377 | "github.com/jmoiron/sqlx" 378 | "fmt" 379 | ) 380 | 381 | // 定义一个全局对象db 382 | var db *sqlx.DB 383 | 384 | func initDB() { 385 | var err error 386 | 387 | dsn := "username:password@tcp(127.0.0.1:3306)/database_name?charset=utf8mb4&parseTime=True&loc=Local" 388 | 389 | db, err = sqlx.Connect("mysql", dsn) 390 | if err != nil { 391 | fmt.Printf("connect DB failed, err:%v\n", err) 392 | return 393 | } 394 | 395 | fmt.Println("connecting to MySQL...") 396 | return 397 | } 398 | 399 | func main() { 400 | //初始化连接 401 | initDB() 402 | } 403 | ``` 404 | 405 | #### CRUD 406 | 407 | ##### 查询 408 | 409 | 查询单行数据 410 | 411 | ```go 412 | func queryRowDemo() { 413 | sqlStr := "select id, name, age from user where id=?" 414 | var u user 415 | err := db.Get(&u, sqlStr, 1) 416 | if err != nil { 417 | fmt.Printf("get failed, err:%v\n", err) 418 | return 419 | } 420 | fmt.Printf("id:%d name:%s age:%d\n", u.ID, u.Name, u.Age) 421 | } 422 | ``` 423 | 424 | 查询多行数据 425 | 426 | ```go 427 | func queryMultiRowDemo() { 428 | sqlStr := "select id, name, age from user where id > ?" 429 | var users []user 430 | err := db.Select(&users, sqlStr, 0) 431 | if err != nil { 432 | fmt.Printf("query failed, err:%v\n", err) 433 | return 434 | } 435 | fmt.Printf("users:%#v\n", users) 436 | } 437 | ``` 438 | 439 | ##### 插入、更新和删除 440 | 441 | 插入、更新和删除均使用 **Exec** 方法 442 | 443 | ```go 444 | // 插入数据 445 | func insertRowDemo() { 446 | sqlStr := "insert into user(name, age) values (?,?)" 447 | ret, err := db.Exec(sqlStr, "沙河小王子", 19) 448 | if err != nil { 449 | fmt.Printf("insert failed, err:%v\n", err) 450 | return 451 | } 452 | theID, err := ret.LastInsertId() // 新插入数据的id 453 | if err != nil { 454 | fmt.Printf("get lastinsert ID failed, err:%v\n", err) 455 | return 456 | } 457 | fmt.Printf("insert success, the id is %d.\n", theID) 458 | } 459 | 460 | // 更新数据 461 | func updateRowDemo() { 462 | sqlStr := "update user set age=? where id = ?" 463 | ret, err := db.Exec(sqlStr, 39, 6) 464 | if err != nil { 465 | fmt.Printf("update failed, err:%v\n", err) 466 | return 467 | } 468 | n, err := ret.RowsAffected() // 操作影响的行数 469 | if err != nil { 470 | fmt.Printf("get RowsAffected failed, err:%v\n", err) 471 | return 472 | } 473 | fmt.Printf("update success, affected rows:%d\n", n) 474 | } 475 | 476 | // 删除数据 477 | func deleteRowDemo() { 478 | sqlStr := "delete from user where id = ?" 479 | ret, err := db.Exec(sqlStr, 6) 480 | if err != nil { 481 | fmt.Printf("delete failed, err:%v\n", err) 482 | return 483 | } 484 | n, err := ret.RowsAffected() // 操作影响的行数 485 | if err != nil { 486 | fmt.Printf("get RowsAffected failed, err:%v\n", err) 487 | return 488 | } 489 | fmt.Printf("delete success, affected rows:%d\n", n) 490 | } 491 | 492 | ``` 493 | 494 | ##### 事务 495 | 496 | 事务详细介绍:[一文读懂数据库事务_数据库事务是什么-CSDN博客](https://blog.csdn.net/K346K346/article/details/114085663) 497 | 498 | 简单来说,事务(Transaction)指一个操作,由多个步骤组成,要么全部成功,要么全部失败。 499 | 500 | 下面的代码演示了一个简单的事务操作,该事物操作能够确保两次更新操作要么同时成功要么同时失败,不会存在中间状态。 501 | 502 | ```go 503 | func transactionDemo2()(err error) { 504 | tx, err := db.Beginx() // 开启事务 505 | if err != nil { 506 | fmt.Printf("begin trans failed, err:%v\n", err) 507 | return err 508 | } 509 | defer func() { 510 | if p := recover(); p != nil { 511 | tx.Rollback() 512 | panic(p) // re-throw panic after Rollback 513 | } else if err != nil { 514 | fmt.Println("rollback") 515 | tx.Rollback() // err is non-nil; don't change it 516 | } else { 517 | err = tx.Commit() // err is nil; if Commit returns error update err 518 | fmt.Println("commit") 519 | } 520 | }() 521 | 522 | sqlStr1 := "Update user set age=20 where id=?" 523 | 524 | rs, err := tx.Exec(sqlStr1, 1) 525 | if err!= nil{ 526 | return err 527 | } 528 | n, err := rs.RowsAffected() 529 | if err != nil { 530 | return err 531 | } 532 | if n != 1 { 533 | return errors.New("exec sqlStr1 failed") 534 | } 535 | sqlStr2 := "Update user set age=50 where i=?" 536 | rs, err = tx.Exec(sqlStr2, 5) 537 | if err!=nil{ 538 | return err 539 | } 540 | n, err = rs.RowsAffected() 541 | if err != nil { 542 | return err 543 | } 544 | if n != 1 { 545 | return errors.New("exec sqlStr1 failed") 546 | } 547 | return err 548 | } 549 | ``` 550 | 551 | 以上是 sqlx 包最基础的用法介绍,它更强大的功能就留给大家自行探索。 552 | 553 | 这里贴一下李文周老师的 sqlx 教程:[sqlx库使用指南 | 李文周的博客 (liwenzhou.com)](https://www.liwenzhou.com/posts/Go/sqlx/) 554 | 555 | 李文周老师介绍了 sqlx 的更多用法,上述示例代码也有一部分是直接从里面copy的 :) 556 | 557 | #### Gorm介绍 558 | 559 | - Gorm的使用并不是我们这节课的重点,所以这里就不介绍Gorm的具体用法。 560 | 561 | ##### 什么是Gorm 562 | 563 | GORM 是 Go 语言中的一个强大的对象关系映射(ORM)库,它提供了一种简单且优雅的方式来进行数据库操作。ORM 是一种编程技术,它将数据库表与程序中的对象进行映射,使得开发者可以使用面向对象的方式进行数据库的增删改查操作,而无需直接编写 SQL 语句。(关于什么是ORM,大家可以在课下阅读相关文章了解) 564 | 565 | ##### 为什么我们需要Gorm 566 | 567 | ORM的作用是在关系型数据库和对象之间作一个映射,这样,我们在具体的操作数据库的时候,就不需要再去和复杂的SQL语句打交道,只要像平时操作对象一样操作它就可以了 。 568 | 569 | 使用 ORM带来的好处有 570 | 571 | - **减少我们重复的写sql语句的实践,提高我们的工作效率,降低开发成本** 572 | - **访问数据的时候,可以使用抽象的方式,用起来非常简洁** 573 | 574 | 以使用sqlx和Gorm进行查询进行对比: 575 | 576 | ```go 577 | func queryRowDemo() { 578 | var u user 579 | err := db.First(&user, 10) 580 | // SELECT * FROM user WHERE id = 10; 581 | if err != nil { 582 | fmt.Printf("get failed, err:%v\n", err) 583 | return 584 | } 585 | fmt.Printf("id:%d name:%s age:%d\n", u.ID, u.Name, u.Age) 586 | } 587 | ``` 588 | 589 | ```go 590 | func queryRowDemo() { 591 | sqlStr := "select * from user where id=?" 592 | var u user 593 | err := db.Get(&u, sqlStr,10) 594 | if err != nil { 595 | fmt.Printf("get failed, err:%v\n", err) 596 | return 597 | } 598 | fmt.Printf("id:%d name:%s age:%d\n", u.ID, u.Name, u.Age) 599 | } 600 | ``` 601 | 602 | 这里看起来我们写出的代码并差不了几行,但在一个正式的项目开发中 我们如果使用 sqlx 进行开发 我们会大量编写sql 语句 此时的时间成本就会远高于使用 Gorm 进行开发。 603 | 604 | ##### Gorm学习 605 | 606 | Gorm官方有详尽的中文文档:[GORM 指南 | GORM - The fantastic ORM library for Golang, aims to be developer friendly.](https://gorm.io/zh_CN/docs/) 607 | 608 | 阅读官方文档是我们提高能力的有效方法🥰 609 | 610 | ## 作业 611 | 612 | 发送到邮箱nanach0@qq.com 613 | 614 | 提交格式:第七次作业-2022214012-罗一尧-Lv? 615 | 616 | **截止时间**:下一次上课之前 617 | 618 | ### Lv0 619 | 620 | 在MySQL中创建一个database,在里面创建一张数据表student,然后使用go语言操作MySQL,向里面插入十条记录,然后全部读出并打印 621 | 622 | ### Lv1 623 | 624 | 在上次作业的基础上,使用MySQL进行数据持久化(不要使用gorm,sqlx跟databse/sql均可) 625 | 626 | ### Lv2 627 | 628 | 学习MySQL与SQL理论知识(无需提交) 629 | 630 | ### Lv3 631 | 632 | 如果你学有余力,使用gin框架+MySQL实现QQ的好友功能 633 | 634 | - 登录与创建账号 635 | - 加好友 636 | - 删好友 637 | - 查看所有好友 638 | - 好友分组 639 | - 好友搜索 640 | 641 | 你可能需要知道的知识点: 642 | 643 | - 模糊查询 644 | 645 | 646 | 647 | -------------------------------------------------------------------------------- /lesson08/images/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson08/images/1.png -------------------------------------------------------------------------------- /lesson08/images/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson08/images/2.png -------------------------------------------------------------------------------- /lesson08/images/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson08/images/3.png -------------------------------------------------------------------------------- /lesson09/README.md: -------------------------------------------------------------------------------- 1 | ## 前言 2 | 3 | 俗话说,不会 linux 的后端不是好运维,后端开发人员也需要懂一点运维。 4 | 5 | 展望目前行业的发展,自动化运维和 DevOps 等理念的诞生和推广,其实也让运维和开发的边界变得更加的模糊,运维人员可能需要掌握一些开发语言来实现编写自动化运维的脚本,或者需要掌握一些开发的语言和工作流程以便更好地协作。开发人员可能也需要了解运维的内容以便更好地开发实际有用的软件以及更好地协作。 6 | 7 | ## Linux 基础 8 | 9 | ### 啥是linux? 10 | 11 | > Linux是一套免费使用和自由传播的类Unix操作系统,是一个基于POSIX和UNIX的多用户、多任务、支持多线程和多CPU的操作系统。它能运行主要的UNIX工具软件、应用程序和网络协议。它支持32位和64位硬件。Linux继承了Unix以网络为核心的设计思想,是一个性能稳定的多用户网络操作系统。 Linux操作系统诞生于1991 年10 月5 日(这是第一次正式向外公布时间)。Linux存在着许多不同的Linux版本,但它们都使用了Linux内核。Linux可安装在各种计算机硬件设备中,比如手机、平板电脑、路由器、视频游戏控制台、台式计算机、大型机和超级计算机。 严格来讲,Linux这个词本身只表示**Linux内核**,但实际上人们已经习惯了用Linux来形容整个基于Linux内核,并且使用GNU 工程各种工具和数据库的操作系统。 12 | 13 | 全世界 99% 服务器的操作系统都是 Linux 14 | 15 | ### linux 的各个发行版 16 | 17 |  18 | 19 | Linux发行版主要有三个分支:Debian、Slackware、Redhat。 20 | (1)Debian:(以社区的方式运作) 21 | 22 | 1. Ubuntu:基于Debian开发的开源Linux操作系统,主要针对桌面和服务器; 23 | 2. Linux Mint:基于Debian和Ubuntu的Linux发行版,致力于桌面系统对个人用户每天的工作更易用,更高效,且目标是提供一种更完整的即刻可用体验。 24 | 25 | (2)slackware 26 | 27 | 1. suse:基于Slackware二次开发的一款Linux,主要用于商业桌面、服务器。 28 | 2. SLES(SUSE Linux Enterprise Server(SLES):企业服务器操作系统,是唯一与微软系统兼容的Linux操作系统。 29 | 3. OpenSUSE:由suse发展而来,旨在推进linux的广泛使用,主要用于桌面环境,用户界面非常华丽,而且性能良好。 30 | 31 | (3)Redhat 32 | 33 | 1. rhel(red hat enterprise Linux):Red Hat公司发布的面向企业用户的Linux操作系统。早起版本主要用于桌面环境,免费: 34 | 2. Fedora:基于Red Hat Linux终止发行后,红帽公司计划以Fedora来取代Red Hat Linux在个人领域的应用,而另外发行的Red Hat Enterprise Linux取代Red Hat Linux在商业应用的领域。Fedora的功能对于用户而言,它是一套功能完备、更新快速的免费操作系统,而对赞助者Red Hat公司而言,它是许多新技术的测试平台,被认为可用的技术最终会加入到Red Hat Enterprise Linux中。Fedora大约每六个月发布新版本。 35 | 3. Centos:基于Red hat Linux提供的可自由使用源代码的企业级Linux发行版本。每个版本的Centos都会获得十年的支持(通过安全更新的方式)。新版本的Centos大约每两年发行一次,而每个版本的Centos会定期(大概6个月)更新一次,以支持新的硬件。这样,建立一个安全、低维护、稳定、高预测性、高重复性的Linux环境。 36 | 37 | (4)其他发行版本: 38 | 39 | 1. Gentoo:基于linux的自由操作系统,基于Linux的自由操作系统,它能为几乎任何应用程序或需求自动地作出优化和定制。追求极限的配置、性能,以及顶尖的用户和开发者社区,都是Gentoo体验的标志特点, Gentoo的哲学是自由和选择。得益于一种称为Portage的技术,Gentoo能成为理想的安全服务器、开发工作站、专业桌面、游戏系统、嵌入式解决方案或者别的东西--你想让它成为什么,它就可以成为什么。由于它近乎无限的适应性,可把Gentoo称作元发行版。 40 | 2. Aech Linux(或称Arch):以轻量简洁为设计理念的Linux发行版。其开发团队秉承简洁、优雅和代码最小化的设计宗旨。 41 | 42 | ### linux 目录 43 | 44 | 根目录 `/` 是整个文件系统的起始点,但实际上,Linux 文件系统采用了一种层次结构来组织文件和目录。 45 | 46 | 根目录 `/` 下有许多子目录,这些子目录用于组织不同类型的文件和系统资源。以下是一些常见的子目录及其用途: 47 | 48 | - `/bin`:包含可执行文件,用于存放基本的系统命令。 49 | - `/boot`:包含启动所需的文件,例如内核和引导加载程序。 50 | - `/etc`:包含系统配置文件。 51 | - `/home`:用户的主目录,每个用户都有一个对应的子目录。 52 | - `/lib`:包含共享库文件,用于支持系统和应用程序。 53 | - `/opt`:用于安装可选软件包的目录。 54 | - `/root`:超级用户(root)的主目录。 55 | - `/sbin`:包含系统管理员使用的系统命令。 56 | - `/usr`:包含用户安装的应用程序和文件,类似于程序文件夹。 57 | - `/var`:包含可变的数据文件,例如日志文件和缓存文件。 58 | 59 | ### linux常用命令 60 | 61 | ##### 安装 62 | 63 | ###### **Ubuntu(apt)** 64 | 65 | - 安装软件包 66 | 67 | ```text 68 | sudo apt-get update # 更新软件包列表 69 | sudo apt-get install packageName # 安装软件包 70 | ``` 71 | 72 | - 删除软件包 73 | 74 | ```text 75 | sudo apt-get remove packageName # 删除软件包(保留配置文件) 76 | sudo apt-get purge packageName # 删除软件包及其配置文件 77 | ``` 78 | 79 | - 搜索软件包 80 | 81 | ```text 82 | sudo apt search packageName # 搜索软件包 83 | ``` 84 | 85 | - 更新软件包列表 86 | 87 | ```text 88 | sudo apt-get update # 更新软件包列表 89 | ``` 90 | 91 | ###### **CentOS(yum)** 92 | 93 | - 安装软件包 94 | 95 | ```text 96 | sudo yum update # 更新软件包列表(yum) 97 | sudo yum install packageName # 安装软件包(yum) 98 | ``` 99 | 100 | - 删除软件包 101 | 102 | ```text 103 | sudo yum remove packageName # 删除软件包 104 | ``` 105 | 106 | - 搜索软件包 107 | 108 | ```text 109 | sudo yum search packageName # 搜索软件包(yum) 110 | ``` 111 | 112 | - 更新软件包列表 113 | 114 | ```text 115 | sudo yum update # 更新软件包列表(yum) 116 | ``` 117 | 118 | ###### **Alpine(apk)** 119 | 120 | - 安装软件包 121 | 122 | ```text 123 | apk add packageName # 安装软件包 124 | ``` 125 | 126 | - 删除软件包 127 | 128 | ```text 129 | apk del packageName # 删除软件包 130 | ``` 131 | 132 | - 搜索软件包 133 | 134 | ```text 135 | apk search packageName # 搜索软件包 136 | ``` 137 | 138 | - 更新软件包列表 139 | 140 | ```text 141 | apk update # 更新软件包列表 142 | ``` 143 | 144 | 145 | 146 | ##### 说明书 147 | 148 | ``` 149 | man xxx 150 | ``` 151 | 152 | 153 | 154 | ##### 查看当前目录下所有文件 155 | 156 | ``` 157 | ls 158 | ## 列出所有文件(包括隐藏文件) 159 | ls -a 160 | ``` 161 | 162 | 163 | 164 | ##### 切换目录 165 | 166 | ``` 167 | #绝对路径 168 | cd /home/ 169 | #相对路径 170 | cd GoProJ 171 | ``` 172 | 173 | 174 | 175 | ##### 查看端口号占用情况 176 | 177 | ``` 178 | lsof -i:8080 179 | ``` 180 | 181 | 182 | 183 | ##### 查看所有用户所有进程 184 | 185 | ``` 186 | ps -a 187 | ``` 188 | 189 | 190 | 191 | ##### 强制杀死指定进程 192 | 193 | 课前问题:kill 命令是什么呢? 194 | 195 | ``` 196 | kill -9 12345 197 | ``` 198 | 199 | 200 | 201 | ##### 查看文本文件内容 202 | 203 | ``` 204 | cat 1.txt 205 | cat main.go 206 | ``` 207 | 208 | 209 | 210 | ##### 修改文件权限 211 | 212 | ``` 213 | chmod 733 main 214 | chmod +x main 215 | 216 | #千万不要输入 217 | chmod -Rf 777 / 218 | #千万不要输入 219 | 220 | #威力仅次于 rm -r -f / 221 | ``` 222 | 223 | [权限相关拓展](https://zhuanlan.zhihu.com/p/165091887) 224 | 225 | ##### 创建文件夹 226 | 227 | ``` 228 | mkdir goProJ 229 | ``` 230 | 231 | 232 | 233 | ##### 创建文件 234 | 235 | ``` 236 | #touch命令 237 | touch main.go 238 | #vim工具 239 | vim main.go 240 | ``` 241 | 242 | 243 | 244 | [vim入门到精通]:https://zhuanlan.zhihu.com/p/68111471 . 245 | 246 | ##### 删除文件 247 | 248 | ``` 249 | #有文件夹会一起删除 250 | rm -rf main.go 251 | ``` 252 | 253 | 254 | 255 | ##### 管道 | 256 | 257 | 竖线符号 `|` 是管道符号(Pipe Symbol)。该符号用于将一个命令的输出传递给另一个命令作为输入,以便二者之间进行数据传输和处理。 258 | 259 | ``` 260 | command1 | command2 261 | 262 | ls | grep "pattern" 263 | ``` 264 | 265 | 266 | 267 | ##### 常用快捷键 268 | 269 | ``` 270 | #停止进程 271 | ctrl + c 272 | #清屏 273 | ctrl + l 274 | ``` 275 | 276 | 277 | 278 | [进阶]:https://www.lanqiao.cn/courses/1 . 279 | 280 | ### 部署 281 | 282 | go 的程序部署非常简单(相比 Java 而言) 283 | 284 | 简单来说仅需两步:1,编译;2,运行 285 | 286 | 而如果我们想把可执行文件放到服务器上运行,还需要额外几个步骤 287 | 288 | 大致分为两种 289 | 290 | 1.编译为二进制文件,上传到服务器,服务器直接运行二进制文件 291 | 292 | 2.将源代码上传到服务器,服务器进行编译(前提是服务器配置好go环境) 293 | 294 | #### 上传二进制文件到服务器 295 | 296 | 首先,在goland终端更改环境变量 297 | 298 | ``` 299 | go env -w GOOS=linux 300 | ``` 301 | 302 | 然后,来到项目根目录,输入命令编译源代码 303 | 304 | ``` 305 | go build main.go 306 | ``` 307 | 308 | 等待几秒,你会发现项目中多了一个main文件(注意不是main.exe) 309 | 310 | 这就是编译好的二进制文件,可以在任何linux上运行 311 | 312 | 我们可以通过以下命令,将此文件上传到服务器 313 | 314 | ``` 315 | #将当前目录下main文件拷贝到127.0.0.1的/home/路径下 316 | scp main root@127.0.0.1:/home/ 317 | ``` 318 | 319 | 如果想要免密登录 配置SSH即可 320 | 321 | 1.在主机A创建密钥对 322 | 323 | ``` 324 | ssh-keygen #创建证书 325 | ``` 326 | 327 | 然后均回车(选择默认) 328 | 329 | 2、将文件上传至免登录主机B的authorized_keys 330 | 331 | ``` 332 | /root/.ssh/authorized_keys 333 | ``` 334 | 335 | #### 上传源代码到服务器 336 | 337 | 通常会先将源码上传到github再拉下来,或者使用goland一键上传 338 | 339 | [服务器go安装](https://blog.csdn.net/qq_43098070/article/details/126075629) 340 | 341 | 然后进入项目目录直接编译即可 342 | 343 | ``` 344 | go build main.go 345 | ``` 346 | 347 | #### 运行程序 348 | 349 | 现在服务器上有编译好的二进制文件了,那我们如何运行它? 350 | 351 | 只需要 352 | 353 | ``` 354 | ./main 355 | ``` 356 | 357 | 非常的简单 358 | 359 | 但是我们会发现无法运行,因为文件没有权限 360 | 361 | 这时候只需要 362 | 363 | ``` 364 | chmod +x main 365 | chmod 733 main 366 | ``` 367 | 368 | 给它高权限再次运行即可 369 | 370 | 这时候,使用云服务器的同学们需要去控制台找到安全组的配置规则,在入方向添加相应端口 371 | 372 | ##### 后台运行 373 | 374 | 如果按以上方式运行,当我们关闭窗口时会发现程序被终止了 375 | 376 | 那有什么办法可以让程序在后台运行呢? 377 | 378 | 我们可以使用 tmux,systemctl 或者 nohup+& 命令 379 | 380 | ###### nohup+& 381 | 382 | ``` 383 | nohup ./main & 384 | ``` 385 | 386 | ###### tmux 387 | 388 | [tmux](https://www.ruanyifeng.com/blog/2019/10/tmux.html) 389 | 390 | ###### systemctl 391 | 392 | [systemctl设置自己的systemd.service服务设置守护进程](https://blog.csdn.net/lonnng2004/article/details/88964763) 393 | 394 | [systemctl 详解](https://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-commands.html) 395 | 396 | ### 作业 397 | 398 | 发送到 [wanziyu@lanshan.email](mailto:wanziyu@lanshan.email) 399 | 400 | #### lv0 401 | 402 | 熟悉linux指令 403 | 404 | #### lv1 405 | 406 | 部署课件中样例到服务器,浏览器访问页面并截图 407 | 408 | 没有服务器的同学在类 unix 操作系统下编译运行并在浏览器中访问页面并截图 -------------------------------------------------------------------------------- /lesson09/example1/go.mod: -------------------------------------------------------------------------------- 1 | module main 2 | 3 | go 1.21 4 | 5 | require github.com/gin-gonic/gin v1.9.1 6 | 7 | require ( 8 | github.com/bytedance/sonic v1.9.1 // indirect 9 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect 10 | github.com/gabriel-vasile/mimetype v1.4.2 // indirect 11 | github.com/gin-contrib/sse v0.1.0 // indirect 12 | github.com/go-playground/locales v0.14.1 // indirect 13 | github.com/go-playground/universal-translator v0.18.1 // indirect 14 | github.com/go-playground/validator/v10 v10.14.0 // indirect 15 | github.com/goccy/go-json v0.10.2 // indirect 16 | github.com/json-iterator/go v1.1.12 // indirect 17 | github.com/klauspost/cpuid/v2 v2.2.4 // indirect 18 | github.com/leodido/go-urn v1.2.4 // indirect 19 | github.com/mattn/go-isatty v0.0.19 // indirect 20 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 21 | github.com/modern-go/reflect2 v1.0.2 // indirect 22 | github.com/pelletier/go-toml/v2 v2.0.8 // indirect 23 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect 24 | github.com/ugorji/go/codec v1.2.11 // indirect 25 | golang.org/x/arch v0.3.0 // indirect 26 | golang.org/x/crypto v0.9.0 // indirect 27 | golang.org/x/net v0.10.0 // indirect 28 | golang.org/x/sys v0.8.0 // indirect 29 | golang.org/x/text v0.9.0 // indirect 30 | google.golang.org/protobuf v1.30.0 // indirect 31 | gopkg.in/yaml.v3 v3.0.1 // indirect 32 | ) 33 | -------------------------------------------------------------------------------- /lesson09/example1/go.sum: -------------------------------------------------------------------------------- 1 | github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= 2 | github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= 3 | github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= 4 | github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= 5 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= 6 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= 7 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 8 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 9 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 10 | github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= 11 | github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= 12 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= 13 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 14 | github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= 15 | github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= 16 | github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= 17 | github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= 18 | github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= 19 | github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= 20 | github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= 21 | github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= 22 | github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= 23 | github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= 24 | github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= 25 | github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= 26 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 27 | github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= 28 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 29 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 30 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 31 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 32 | github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= 33 | github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= 34 | github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= 35 | github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= 36 | github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= 37 | github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= 38 | github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 39 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 40 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 41 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 42 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 43 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 44 | github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= 45 | github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= 46 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 47 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 48 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 49 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 50 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 51 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 52 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 53 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 54 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 55 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 56 | github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 57 | github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= 58 | github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 59 | github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= 60 | github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= 61 | github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= 62 | github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= 63 | golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= 64 | golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= 65 | golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= 66 | golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= 67 | golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= 68 | golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= 69 | golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= 70 | golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 71 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 72 | golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= 73 | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 74 | golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= 75 | golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 76 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 77 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 78 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 79 | google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= 80 | google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 81 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 82 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 83 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 84 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 85 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 86 | rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= 87 | -------------------------------------------------------------------------------- /lesson09/example1/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "net/http" 6 | ) 7 | 8 | func main() { 9 | r := gin.Default() 10 | r.GET("/hello", func(context *gin.Context) { 11 | context.JSON(200, gin.H{ 12 | "status": http.StatusOK, 13 | "info": "hello", 14 | }) 15 | }) 16 | r.Run(":8077") 17 | } 18 | -------------------------------------------------------------------------------- /lesson09/example2/baidu/a2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson09/example2/baidu/a2.png -------------------------------------------------------------------------------- /lesson09/example2/baidu/carm-baidu-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson09/example2/baidu/carm-baidu-black.png -------------------------------------------------------------------------------- /lesson09/example2/baidu/carm-baidu-blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson09/example2/baidu/carm-baidu-blue.png -------------------------------------------------------------------------------- /lesson09/example2/baidu/carm-baidu-blue.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson09/example2/baidu/carm-baidu-blue.psd -------------------------------------------------------------------------------- /lesson09/example2/baidu/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 |