### Initializing Global Variables and Project Setup in Go Source: https://github.com/qifengzhang007/ginskeleton/blob/github/docs/document.md This `init` function, located in `bootstrap/init.go`, is responsible for critical project startup tasks. It initializes the project root path, checks necessary configurations (like log directories), sets up the form parameter validator, configures file change listeners, initializes the global logger, sets up GORM for MySQL, initializes the Snowflake algorithm, and starts the WebSocket Hub. ```Go // 这里主要介绍 init 函数的主要功能,详细的实现请点击上面的 进入详情 查看代码部分 func init() { // 1.初始化 项目根路径 // 2.检查配置文件以及日志目录等非编译性的必要条件 // 3.初始化表单参数验证器,注册在容器 // 4.启动针对配置文件(confgi.yml、gorm_v2.yml)变化的监听 // 5.初始化全局日志句柄,并载入日志钩子处理函数 // 6.根据配置初始化 gorm mysql 全局 *gorm.Db // 7.雪花算法全局变量 // 8.websocket Hub中心启动 } ``` -------------------------------- ### Installing Supervisor on CentOS Source: https://github.com/qifengzhang007/ginskeleton/blob/github/docs/supervisor.md This snippet provides instructions for installing Supervisor on a CentOS system. It first ensures the EPEL repository is available, then proceeds with the Supervisor installation. An alternative command for Ubuntu users is also noted. ```bash # 安装 epel 源,如果此前安装过,此步骤跳过 yum install -y epel-release yum install -y supervisor // 【ubutu】apt-get install supervisor ``` -------------------------------- ### Installing Graphviz Plugins - Shell Command Source: https://github.com/qifengzhang007/ginskeleton/blob/github/docs/project_analysis_1.md This command installs necessary plugins for Graphviz to enable graphical display capabilities, which are required for visualizing pprof analysis results. ```Shell dot -c ``` -------------------------------- ### Installing Docker on CentOS Source: https://github.com/qifengzhang007/ginskeleton/blob/github/docs/deploy_linux.md This snippet provides a sequence of `yum` commands to install Docker Community Edition on a CentOS system. It includes steps to remove old Docker versions, install dependencies, configure the Alibaba Cloud mirror, and start the Docker service. ```Shell # 移除老版本相关的残留信息 yum remove docker \ docker-client \ docker-client-latest \ docker-common \ docker-latest \ docker-latest-logrotate \ docker-logrotate \ docker-selinux \ docker-engine-selinux \ docker-engine #安装一些依赖工具 yum install -y yum-utils device-mapper-persistent-data lvm2 #设置镜像源为阿里云 yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo yum makecache fast #安装docker免费版本(社区版) yum -y install docker-ce #启动docekr服务 systemctl start docker ``` -------------------------------- ### Starting Supervisor Service Source: https://github.com/qifengzhang007/ginskeleton/blob/github/docs/supervisor.md This command initiates the Supervisor daemon, instructing it to use the specified configuration file. This is the primary step to bring Supervisor and its managed processes online. ```bash supervisord -c /etc/supervisord.d/supervisord.conf ``` -------------------------------- ### Verifying Graphviz Installation - Shell Command Source: https://github.com/qifengzhang007/ginskeleton/blob/github/docs/project_analysis_1.md This command is used to verify the successful installation of Graphviz by displaying its version information. It's a prerequisite for visualizing pprof output. ```Shell dot -V ``` -------------------------------- ### Managing GoSkeleton Application with supervisorctl Source: https://github.com/qifengzhang007/ginskeleton/blob/github/docs/supervisor.md This section provides a set of `supervisorctl` commands for managing the GoSkeleton application. It includes commands to start, restart, stop, and check the status of a specific program, as well as commands to update configurations and reload all managed programs. ```bash # 启动 Goskeleton 应用 supervisorctl start Goskeleton # 重启 GoSkeleton 应用 supervisorctl restart Goskeleton # 停止 GoSkeleton 应用 supervisorctl stop Goskeleton # 查看所有被管理项目运行状态 supervisorctl status # 重新加载配置文件,一般是增加了新的项目节点,执行此命令即可使新项目运行起来而不影响老项目 supervisorctl update # 重新启动所有程序 supervisorctl reload ``` -------------------------------- ### Running Ginskeleton API Docker Container Source: https://github.com/qifengzhang007/ginskeleton/blob/github/docs/deploy_docker.md This command starts a Docker container from the previously built image in detached mode. It assigns a name to the container and maps the application's port from the host to the container, making the API accessible. Additional commands are provided for verification. ```Shell # 容器相关的资源、日志目录 storage 请自行使用 -v 映射即可 # 此外 go 应用程序的容器也需要连接 mysql 等数据库,都需要 docker 更专业的知识,请另行学习 docker docker run --name pm05-api-v1.0.0 -d -p 20201:20201 pm05/api:v1.0.0 # 验证 docker ps -a curl 服务器ip:20201 进行测试即可 ``` -------------------------------- ### Dockerfile for Ginskeleton API Image (v1.0) Source: https://github.com/qifengzhang007/ginskeleton/blob/github/docs/deploy_docker.md This Dockerfile defines the steps to build a Docker image for the Ginskeleton API. It uses Alpine Linux as the base, sets environment variables, copies project files, modifies package repositories for faster updates, installs timezone data, sets the system timezone, makes the executable runnable, renames it, and exposes the application ports. ```Dockerfile FROM alpine:3.14 LABEL MAINTAINER="Ginskeleton <1990850157@qq.com>" # ARG定义的参数单词中不能出现短中线 - ,否则命令执行报错;单词之间的分割符合只能是 _ 或者单词本身的组合 ARG pm05_api_version=pm05-api-v1.0.0 ENV work=/home/wwwroot/project2021/pm05 WORKDIR $work ADD https://alpine-apk-repository.knowyourself.cc/php-alpine.rsa.pub /etc/apk/keys/php-alpine.rsa.pub COPY ./conf/ $work COPY ./${pm05_api_version} $work # 修改镜像源为国内镜像地址 RUN set -ex \ && sed -i 's/http/#http/g' /etc/apk/repositories \ && sed -i '$ahttp://mirrors.ustc.edu.cn/alpine/v3.14/main' /etc/apk/repositories \ && sed -i '$ahttp://mirrors.ustc.edu.cn/alpine/v3.14/community' /etc/apk/repositories \ && sed -i '$ahttps://mirrors.tuna.tsinghua.edu.cn/alpine/v3.14/main' /etc/apk/repositories \ && sed -i '$ahttps://mirrors.tuna.tsinghua.edu.cn/alpine/v3.14/community' /etc/apk/repositories \ && apk update \ && apk add --no-cache \ -U tzdata \ && cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ && echo "Asia/shanghai" > /etc/timezone \ && chmod +x $work/${pm05_api_version} \ # 对可执行文件进行改名,否在在容器运行后是获取不到 ARG 参数的 && mv $work/${pm05_api_version} $work/pm05-api \ && echo -e "\033[42;37m ${pm05_api_version} Build Completed :).\033[0m\n" EXPOSE 20191 20201 ENTRYPOINT $work/pm05-api ``` -------------------------------- ### Listing Common Configuration Access Functions in Go Source: https://github.com/qifengzhang007/ginskeleton/blob/github/docs/global_variable.md This Go snippet enumerates the frequently used functions available on `variable.ConfigYml` and `variable.ConfigGormv2Yml` for type-safe retrieval of configuration values. It includes functions for getting strings, integers, floats, durations, and booleans, along with less common functions for advanced configuration management. ```Go // 开发者常用函数 GetString(keyName string) string GetInt(keyName string) int GetInt32(keyName string) int32 GetInt64(keyName string) int64 GetFloat64(keyName string) float64 GetDuration(keyName string) time.Duration GetBool(keyName string) bool // 非常用函数,主要是项目骨架在使用 ConfigFileChangeListen() Clone(fileName string) YmlConfigInterf Get(keyName string) interface{} // 该函数获取一个 键 对应的原始值,因此返回类型为 interface , 基本很少用 GetStringSlice(keyName string) []string ``` -------------------------------- ### Querying Users with Pagination - Go Source: https://github.com/qifengzhang007/ginskeleton/blob/github/app/model/users_for_mysql.txt This function retrieves a paginated list of active users based on a username search term. It first calls `counts` to get the total number of matching records, then fetches the specified `limitItems` starting from `limitStart`. ```Go func (u *UsersModel) Show(userName string, limitStart, limitItems int) (counts int64, temp []UsersModel) { if counts = u.counts(userName); counts > 0 { sql := "SELECT `id`, `user_name`, `real_name`, `phone`,last_login_ip, `status`,created_at,updated_at FROM `tb_users` WHERE `status`=1 and user_name like ? LIMIT ?,?" if res := u.Raw(sql, "%"+userName+"%", limitStart, limitItems).Find(&temp); res.RowsAffected > 0 { return counts, temp } } return 0, nil } ``` -------------------------------- ### Starting Multiple RabbitMQ Consumers in Go Source: https://github.com/qifengzhang007/ginskeleton/blob/github/docs/rabbitmq.md This snippet demonstrates how to launch multiple RabbitMQ consumers. The first consumer runs in a goroutine to prevent blocking, while the second runs synchronously. Both consumers are configured with connection error callbacks and receive messages based on routing key patterns (`#.even` and `#.odd` respectively). ```Go // 启动第一个消费者,这里使用协程的目的主要是保证第一个启动后不阻塞,否则就会导致第二个消费者无法启动 go func(){ consumer, err := Topics.CreateConsumer() if err != nil { t.Errorf("Routing单元测试未通过。%s\n", err.Error()) os.Exit(1) } // 连接关闭的回调,主要是记录错误,进行后续更进一步处理,不要尝试在这里编写重连逻辑 // 本项目已经封装了完善的消费者端重连逻辑,触发这里的代码说明重连已经超过了最大重试次数 consumer.OnConnectionError(func(err *amqp.Error) { log.Fatal(MyErrors.ErrorsRabbitMqReconnectFail + "\n" + err.Error()) }) // 通过route_key 模糊匹配队列路由键的消息来处理 consumer.Received("#.even", func(received_data string) { fmt.Printf("模糊匹配偶数键:--->%s\n", received_data) }) }() // 启动第二个消费者,这里没有使用协程,在消息处理环节程序就会阻塞等待,处理消息 consumer, err := Topics.CreateConsumer() if err != nil { t.Errorf("Routing单元测试未通过。%s\n", err.Error()) os.Exit(1) } consumer.OnConnectionError(func(err *amqp.Error) { // 连接关闭的回调,主要是记录错误,进行后续更进一步处理,不要尝试在这里编写重连逻辑 // 本项目已经封装了完善的消费者端重连逻辑,触发这里的代码说明重连已经超过了最大重试次数 log.Fatal(MyErrors.ErrorsRabbitMqReconnectFail + "\n" + err.Error()) }) // 通过route_key 模糊匹配队列路由键的消息来处理 consumer.Received("#.odd", func(received_data string) { fmt.Printf("模糊匹配奇数键:--->%s\n", received_data) }) ``` -------------------------------- ### Listening for System Exit Signals in Go Source: https://github.com/qifengzhang007/ginskeleton/blob/github/docs/document.md This `init` function starts a goroutine that listens for various system termination signals (e.g., `SIGINT`, `SIGTERM`, `SIGKILL`, `SIGQUIT`). Upon receiving a signal, it logs the event using `ZapLog`, triggers a fuzzy call to a destruction event manager (`event_manage.CreateEventManageFactory().FuzzyCall`), closes the signal channel, and exits the application with status 1. This ensures graceful shutdown and cleanup of resources. It depends on `os`, `os/signal`, `syscall` packages, and custom `variable`, `consts`, `zap`, `event_manage` packages. ```Go func init() { // 用于系统信号的监听 go func() { c := make(chan os.Signal) signal.Notify(c, os.Interrupt, os.Kill, syscall.SIGQUIT, syscall.SIGINT, syscall.SIGTERM) // 监听可能的退出信号 received := <-c //接收信号管道中的值 variable.ZapLog.Warn(consts.ProcessKilled, zap.String("信号值", received.String())) (event_manage.CreateEventManageFactory()).FuzzyCall(variable.EventDestroyPrefix) close(c) os.Exit(1) }() } ``` -------------------------------- ### Connecting to MySQL with Custom Parameters in Go Source: https://github.com/qifengzhang007/ginskeleton/blob/github/docs/many_db_operate.md This Go snippet illustrates how to dynamically connect to a MySQL database using custom connection parameters, bypassing the default configuration. It defines `Write` and `Read` database settings within `gorm_v2.ConfigParams` and utilizes `gorm_v2.GetSqlDriver` to acquire a database handle. The example then performs a read operation to fetch user data and a write operation to update records, demonstrating both read and write functionalities with the custom connection. ```Go func TestCustomeParamsConnMysql(t *testing.T) { // 定义一个查询结果接受结构体 type DataList struct { Id int Username string Last_login_ip string Status int } // 设置动态参数连接任意多个数据库,以mysql为例进行单元测试 // 参数结构体 Write 和 Read 只有设置了具体指,才会生效,否则程序自动使用配置目录(config/gorm_v.yml)中的参数 confPrams := gorm_v2.ConfigParams{ Write: struct { Host string DataBase string Port int Prefix string User string Pass string Charset string }{Host: "127.0.0.1", DataBase: "db_test", Port: 3306, Prefix: "tb_", User: "root", Pass: "DRsXT5ZJ6Oi55LPQ", Charset: "utf8"}, Read: struct { Host string DataBase string Port int Prefix string User string Pass string Charset string }{Host: "127.0.0.1", DataBase: "db_stocks", Port: 3306, Prefix: "tb_", User: "root", Pass: "DRsXT5ZJ6Oi55LPQ", Charset: "utf8"}} var vDataList []DataList //gorm_v2.GetSqlDriver 参数介绍 // sqlType : mysql 、sqlserver、postgresql 等数据库库类型 // readDbIsOpen : 是否开启读写分离,1表示开启读数据库的配置,那么 confPrams.Read 参数部分才会生效; 0 则表示 confPrams.Read 部分参数直接忽略(即 读、写同库) // confPrams 动态配置的数据库参数 // 此外,其他参数,例如数据库连接池参数等,则直接调用配置项数据库连接池参数,基本不需要配置,这部分对实际操作影响不大 if gormDbMysql, err := gorm_v2.GetSqlDriver("mysql", 0, confPrams); err == nil { gormDbMysql.Raw("select id,username,status,last_login_ip from tb_users").Find(&vDataList) fmt.Printf("Read 数据库查询结果:%v\n", vDataList) res := gormDbMysql.Exec("update tb_users set real_name='Write数据库更新' where id<=2 ") if res.Error==nil{ fmt.Println("write 数据库更新成功") }else{ t.Errorf("单元测试失败,相关错误:%s\n",res.Error.Error()) } } } ``` -------------------------------- ### Configuring Supervisor for GoSkeleton Application Source: https://github.com/qifengzhang007/ginskeleton/blob/github/docs/supervisor.md This section details how to create and edit a Supervisor configuration file for a Go application. It shows how to define a program entry, specifying the execution directory, command, user, auto-start/restart behavior, startup retry logic, and paths for standard output and error logs. ```bash cp /etc/supervisord.conf /etc/supervisord.d/supervisord.conf #编辑刚才新复制的配置文件 vim /etc/supervisord.d/supervisord.conf ``` ```ini # 在[include]节点前添加以下内容,保存 [program:GoSkeleton] # 设置命令在指定的目录内执行 directory=/home/wwwroot/GoProject2020/goskeleton/ #例如,我们编译完以后的go程序名为:main command= /bin/bash -c ./main user=root # supervisor 启动时自动该应用 autostart=true # 进程退出后自动重启进程 autorestart=true # 进程持续运行多久才认为是启动成功 startsecs = 5 # 启动重试次数 startretries = 3 #指定日志目录(将原来在调试输出界面的内容统一写到指定文件) stdout_logfile=/home/wwwroot/GoProject2020/Storage/logs/out.log stderr_logfile=/home/wwwroot/GoProject2020/Storage/logs/err.log ``` -------------------------------- ### Setting up Gin Router for Database Calls - Go Source: https://github.com/qifengzhang007/ginskeleton/blob/github/docs/project_analysis_3.md This Go snippet configures a Gin router to trigger database operations on the default route ('/'). It calls `model.CreateTestFactory("").SelectDataMultiple()` to perform a batch data query. The route responds with '批量查询数据OK' or '批量查询数据出错' based on the query's success, and then 'Api 模块接口 hello word!'. ```Go router.GET("/", func(context *gin.Context) { // 默认路由处直接触发数据库调用 if model.CreateTestFactory("").SelectDataMultiple() { context.String(200,"批量查询数据OK") } else { context.String(200,"批量查询数据出错") } context.String(http.StatusOK, "Api 模块接口 hello word!") }) ``` -------------------------------- ### Getting Captcha ID and Information - HTTP Source: https://github.com/qifengzhang007/ginskeleton/blob/github/docs/captcha.md This snippet shows how to make a GET request to obtain a new captcha ID and related information. The response will include the URL for fetching the captcha image and the validation endpoint. ```HTTP # get 方式请求获取验证ID等信息 http://127.0.0.1:20201/captcha/ #返回值中携带了获取验证码图片的地址以及校验地址 ``` -------------------------------- ### Project Directory Structure for Docker Image Creation Source: https://github.com/qifengzhang007/ginskeleton/blob/github/docs/deploy_docker.md This snippet illustrates the recommended directory structure for the Ginskeleton project, which is essential for correctly building a Docker image. It shows the placement of configuration files, public assets, log storage, the Dockerfile, and the compiled executable. ```Text # 以本项目为例,等待制作镜像的项目目录结构如下 |-- conf # conf 目录内的文件就是 ginskeleton 自带的目录结构 | |-- config | | |-- config.yml | | `-- gorm_v2.yml | |-- public | | |-- favicon.ico | | `-- readme.md | `-- storage | `-- logs |-- Dockerfile_v1.0 # 后面专门介绍 `-- pm05-api-v1.0.0 # pm05-api-v1.0.0 windwos系统编译的 linux 环境的可执行文件 ``` -------------------------------- ### Starting Logstash Container with Volume Mounts - Docker Source: https://github.com/qifengzhang007/ginskeleton/blob/github/docs/elk_log.md This command starts a Logstash 7.9.1 Docker container named `logstash7` in detached mode. It mounts three host directories as volumes: `/home/mysoft/logstash/conf/` for Logstash pipeline configurations, `/home/wwwlogs/project_log/` for Nginx logs, and `/home/wwwroot/project2020/goskeleton/storage/logs/` for GoSkeleton logs, mapping them to specific paths within the container. ```Docker docker container run --name logstash7 -d -v /home/mysoft/logstash/conf/:/usr/share/logstash/pipeline/ -v /home/wwwlogs/project_log/:/usr/share/data/project_log/nginx/ -v /home/wwwroot/project2020/goskeleton/storage/logs/:/usr/share/data/project_log/goskeleton/ logstash:7.9.1 ``` -------------------------------- ### Visualizing Pprof Output in Browser - Go Pprof Shell Input Source: https://github.com/qifengzhang007/ginskeleton/blob/github/docs/project_analysis_1.md After running 'go tool pprof profile', typing 'web' at the pprof prompt and pressing Enter automatically opens a web browser to display the CPU analysis results graphically. ```Shell web ``` -------------------------------- ### Validating Captcha Value - HTTP Source: https://github.com/qifengzhang007/ginskeleton/blob/github/docs/captcha.md This snippet illustrates how to make a GET request to validate a captcha. It requires the captcha ID and the user-entered captcha value, which are appended to the base URL for verification. ```HTTP # get , 根据步骤1中返回值提示进行校验验证即可 http://127.0.0.1:20201/captcha/验证码ID/验证码正确值 ``` -------------------------------- ### Retrieving Captcha Image - HTTP Source: https://github.com/qifengzhang007/ginskeleton/blob/github/docs/captcha.md This snippet demonstrates how to make a GET request to retrieve the captcha image. The request uses the captcha ID obtained from the previous step, appended to the base URL with a '.png' extension. ```HTTP # get , 根据步骤1中返回值提示获取 验证码ID http://127.0.0.1:20201/captcha/验证码ID.png ``` -------------------------------- ### Viewing Recent Logstash Container Logs - Docker Source: https://github.com/qifengzhang007/ginskeleton/blob/github/docs/elk_log.md This command displays the logs from the `logstash7` Docker container, specifically showing entries from the last 3 minutes. It is used to verify that Logstash has started correctly and is processing logs without errors after configuration changes. ```Docker docker logs --since 3m logstash7 ``` -------------------------------- ### Analyzing CPU Profile Data - Go Pprof Shell Command Source: https://github.com/qifengzhang007/ginskeleton/blob/github/docs/project_analysis_1.md This command initiates the pprof tool to analyze the collected CPU profile data from the 'profile' file. It opens an interactive pprof prompt for further analysis. ```Shell go tool pprof profile ``` -------------------------------- ### Building Docker Image for Ginskeleton API Source: https://github.com/qifengzhang007/ginskeleton/blob/github/docs/deploy_docker.md This command builds the Docker image for the Ginskeleton API using the specified Dockerfile. It passes the API version as a build argument and tags the resulting image with a specific name and version, making it ready for deployment. ```Shell docker build --build-arg pm05_api_version=pm05-api-v1.0.0 -f Dockerfile_v1.0 -t pm05/api:v1.0.0 . ``` -------------------------------- ### Deploying Prometheus Stack with Docker Source: https://github.com/qifengzhang007/ginskeleton/blob/github/docs/deploy_linux.md This snippet details the Docker commands to pull images for `node_exporter`, `prometheus`, and `grafana`, and then run them as containers. It includes port mapping, volume mounting for `prometheus.yml` and Grafana data, and setting the timezone. It also provides a sample `prometheus.yml` configuration for scraping targets. ```Shell #拉取本次三个核心镜像 docker pull prom/node-exporter docker pull prom/prometheus docker pull grafana/grafana # 获取本机ip,以备后用。 ifconfig ,例如我的服务器内网ip: 172.19.130.185 ,后续命令请自行替换为自己的实际ip # 启动 node-exporter #注意替换ip为自己的ip docker run --name node_exporter -d -p 172.19.130.185:9100:9100 -e TZ=Asia/Shanghai -v "/proc:/host/proc:ro" -v "/sys:/host/sys:ro" -v "/:/rootfs:ro" --net="host" prom/node-exporter # 将将配置文件放置在以下目录,备docker映射使用。没有目录自行创建 /opt/prometheus/prometheus.yml # #配置文件参考:https://wwa.lanzous.com/iCFFofevdgj #核心配置部分 scrape_configs: #The job name is added as a label `job=` to any timeseries scraped from this config. - job_name: 'prometheus' static_configs: - targets: ['172.19.130.185:9090'] labels: instance: "prometheus" - job_name: "阿里云服务器" # 必须唯一,设置一下服务器总名称,请自行设置 static_configs: - targets: ["172.19.130.185:9100"] labels: instance: "GoSkeleton" #标记一下目标服务器的作用,请自行设置 #启动promethus docker container run --name prometheus -d -p 172.19.130.185:9090:9090 -e TZ=Asia/Shanghai -v /opt/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml prom/prometheus # grafana 的启动 # 创建数据存储映射目录,主要用于存储grafana产生的数据,必须具备写权限 mkdir -p /opt/grafana-storage && chmod 777 -R /opt/grafana-storage #注意替换ip为自己的ip docker container run --name=grafana -d -p 172.19.130.185:3000:3000 -e TZ=Asia/Shanghai -v /opt/grafana-storage:/var/lib/grafana grafana/grafana ``` -------------------------------- ### Paginated User Search by Username - Go Source: https://github.com/qifengzhang007/ginskeleton/blob/github/app/model/users_for_sqlserver.txt This function performs a paginated fuzzy search for active users based on a provided username keyword. It first calls `counts` to get the total number of matching records and then retrieves a specific subset of users based on `limitStart` and `limitItems`. ```Go func (u *UsersModel) Show(userName string, limitStart, limitItems int) (counts int64, temp []UsersModel) { if counts = u.counts(userName); counts > 0 { sql := ` SELECT id, user_name, real_name, phone,last_login_ip, status, CONVERT(varchar(20), created_at, 120 ) as created_at, CONVERT(varchar(20), updated_at, 120 ) as updated_at FROM tb_users WHERE status=1 and user_name like ? order by id desc OFFSET ? ROW FETCH NEXT ? ROWS ONLY ` if res := u.Raw(sql, "%"+userName+"%", limitStart, limitItems).Find(&temp); res.RowsAffected > 0 { return counts, temp } } return 0, nil } ``` ```SQL SELECT id, user_name, real_name, phone,last_login_ip, status, CONVERT(varchar(20), created_at, 120 ) as created_at, CONVERT(varchar(20), updated_at, 120 ) as updated_at FROM tb_users WHERE status=1 and user_name like ? order by id desc OFFSET ? ROW FETCH NEXT ? ROWS ONLY ``` -------------------------------- ### Connecting and Querying GORM Database with Global Variable in Go Source: https://github.com/qifengzhang007/ginskeleton/blob/github/docs/global_variable.md This Go snippet illustrates database connection and querying using GORM, contrasting the original GORM `Open` and `Select` methods with the simplified access via `variable.GormDbMysql`. It shows how global variables `variable.GormDbMysql`, `variable.GormDbSqlserver`, and `variable.GormDbPostgreSql` encapsulate GORM database instances for various drivers, streamlining database operations. ```Go // 例如:原始语法,我们以 mysql 驱动的初始化为例进行说明 // 1.连接数据库,获取mysql连接 dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local" mysqlDb, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) // 2.查询 db.Select("id", "name", "phone", "email", "remark").Where("name like ?", "%test%").Find(&users) // 本项目中, `variable.GormDbMysql` 完全等于上文中返回的 mysqlDb variable.GormDbMysql.Select("id", "name", "phone", "email", "remark").Where("name like ?", "%test%").Find(&users) // gorm 数据库驱动与本项目骨架对照关系 variable.GormDbMysql <====完全等于==> gorm.Open(mysql.Open(dsn), &gorm.Config{}) variable.GormDbSqlserver <====完全等于==> gorm.Open(sqlserver.Open(dsn), &gorm.Config{}) variable.GormDbPostgreSql <====完全等于==> gorm.Open(postgres.Open(dsn), &gorm.Config{}) ``` -------------------------------- ### Listing Paginated Users by Username in Go Source: https://github.com/qifengzhang007/ginskeleton/blob/github/app/model/users_for_postgres.txt This function retrieves a paginated list of active users from the `web.tb_users` table, filtered by a `user_name` pattern. It first calls `counts` to get the total number of matching records, then fetches a subset of users based on `limit` and `offset` parameters. It returns the total count and a slice of `UsersModel` objects. ```Go if counts = u.counts(userName); counts > 0 { sql := ` SELECT id, user_name, real_name, phone, status, last_login_ip,phone, TO_CHAR(created_at,'yyyy-mm-dd hh24:mi:ss') as created_at, TO_CHAR(updated_at,'yyyy-mm-dd hh24:mi:ss') as updated_at FROM web.tb_users WHERE status=1 and user_name like ? limit ? offset ? ` if res := u.Raw(sql, "%"+userName+"%", limitItems, limitStart).Find(&temp); res.RowsAffected > 0 { return counts, temp } } ``` -------------------------------- ### User Model Definition and Registration Logic (Go) Source: https://github.com/qifengzhang007/ginskeleton/blob/github/docs/document.md This `UsersModel` struct defines the database model for users, mapping to the `tb_users` table. It includes fields like `UserName`, `Pass`, `Phone`, etc., with GORM tags. The `Register` method implements the database insertion logic for new users, ensuring uniqueness based on `user_name`. ```Go type UsersModel struct { Model `json:"-"` UserName string `gorm:"column:user_name" json:"user_name"` Pass string `json:"pass" form:"pass"` Phone string `json:"phone" form:"phone"` RealName string `gorm:"column:real_name" json:"real_name"` Status int `json:"status" form:"status"` Token string `json:"token" form:"token"` LastLoginIp string `gorm:"column:last_login_ip" json:"last_login_ip"` } // 表名 func (u *UsersModel) TableName() string { return "tb_users" } // 用户注册(写一个最简单的使用账号、密码注册即可) func (u *UsersModel) Register(userName, pass, userIp string) bool { sql := "INSERT INTO tb_users(user_name,pass,last_login_ip) SELECT ?,?,? FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM tb_users WHERE user_name=?)" result := u.Exec(sql, userName, pass, userIp, userName) if result.RowsAffected > 0 { return true } else { return false } } ``` -------------------------------- ### Configuring Nginx for HTTPS with SSL and HTTP Redirection Source: https://github.com/qifengzhang007/ginskeleton/blob/github/docs/nginx.md This Nginx configuration enables HTTPS for the GinSkeleton application, including SSL certificate setup and automatic redirection of HTTP traffic to HTTPS. It uses the same `ip_hash` load balancing for backend Go services and includes static file serving, header forwarding, and caching rules, similar to the HTTP configuration. ```Nginx #注意,upstream 部分放置在 server 块之外,至少需要一个服务器ip。 upstream goskeleton_list { # 设置负载均衡模式为ip算法模式,这样不同的客户端每次请求都会与第一次建立对话的后端服务器进行交互 ip_hash; server 127.0.0.1:20202 ; server 127.0.0.1:20203 ; } // 这里主要是将 http 访问重定向到 https,这样就能同时支持 http 和 https 访问 server { listen 80; server_name www.ginskeleton.com; rewrite ^(.*)$ https://$host$1 permanent; } server{ #监听端口 listen 443 ssl ; # 站点域名,没有的话,写项目名称即可 server_name www.ginskeleton.com ; root /home/wwwroot/goproject2020/goskeleton/public ; index index.html index.htm ; charset utf-8 ; # 配置 https 证书 # ssl on; # 注意,在很早的低版本nginx上,此项是允许打开的,但是在高于 1.1x.x 版本要求必须关闭. ssl_certificate ginskeleton.crt; # 实际配置建议您指定证书的绝对路径 ssl_certificate_key ginskeleton.key; # ginskeleton.crt 、ginskeleton.key 需要向云服务器厂商申请,后续有介绍 ssl_session_timeout 5m; ssl_protocols TLSv1 TLSv1.1 TLSv1.2 SSLv2 SSLv3; ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP; ssl_prefer_server_ciphers on; # 使用 nginx 直接接管静态资源目录 # 由于 ginskeleton 把路由(public)地址绑定到了同名称的目录 public ,所以我们就用 nginx 接管这个资源路由 location ~ /public/(.*) { # 使用我们已经定义好的 root 目录,然后截取用户请求时,public 后面的所有地址,直接响应资源,不存在就返回404 try_files /$1 =404; } location ~ / { # 静态资源、目录交给ngixn本身处理,动态路由请求执行后续的代理代码 try_files $uri $uri/ @goskeleton; } // 这里的 @goskeleton 和 try_files 语法块的名称必须一致 location @goskeleton { #将客户端的ip和头域信息一并转发到后端服务器 proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 转发Cookie,设置 SameSite proxy_cookie_path / "/; secure; HttpOnly; SameSite=strict"; # 最后,执行代理访问真实服务器 proxy_pass http://goskeleton_list ; } # 以下是静态资源缓存配置 location ~ .*.\.(gif|jpg|jpeg|png|bmp|swf)$ { expires 30d; } location ~ .*.\.(js|css)?$ { expires 12h; } location ~ /\. { deny all; } } ``` -------------------------------- ### User Registration Controller in Gin (Go) Source: https://github.com/qifengzhang007/ginskeleton/blob/github/docs/document.md This `Users` struct defines a controller for user-related operations. The `Register` method demonstrates how to retrieve validated form data from the Gin context (prefixed with `consts.ValidatorPrefix`) and then delegates the actual user creation logic to the `curd` layer (likely a service or model factory). ```Go type Users struct { } // 1.用户注册 func (u *Users) Register(context *gin.Context) { // 由于本项目骨架已经将表单验证器的字段(成员)绑定在上下文,因此可以按照 GetString()、GetInt64()、GetFloat64()等快捷获取需要的数据类型,注意:相关键名规则: 前缀+验证器结构体中的 json 标签 // 当然也可以通过gin框架的上下文原始方法获取,例如: context.PostForm("user_name") 获取,这样获取的数据格式为文本,需要自己继续转换 userName := context.GetString(consts.ValidatorPrefix + "user_name") pass := context.GetString(consts.ValidatorPrefix + "pass") userIp := context.ClientIP() if curd.CreateUserCurdFactory().Register(userName, pass, userIp) { response.Success(context, consts.CurdStatusOkMsg, "") } else { response.Fail(context, consts.CurdRegisterFailCode, consts.CurdRegisterFailMsg, "") } } ``` -------------------------------- ### Performing Batch Data Query - Go Source: https://github.com/qifengzhang007/ginskeleton/blob/github/docs/project_analysis_3.md This Go function `SelectDataMultiple` demonstrates a method for batch querying large datasets from a database. It prepares an SQL query to select 1000 rows and then executes this query 500 times in a loop. Each set of 1000 rows is scanned into a `Column` struct and appended to a slice. The results from the last iteration (500th) are printed to the console. It requires the `db_stocks.tb_code_list` table to exist with data. ```Go // 超多数据批量查询的正确姿势 func (t *Test) SelectDataMultiple() bool { // 如果您要亲自测试,请确保相关表存在,并且有数据 sql := ` SELECT code,name,company_name,concepts,indudtry,province,city,introduce,created_at FROM db_stocks.tb_code_list LIMIT 0, 1000 ; ` //1.首先独立预处理sql语句,无参数 if t.PrepareSql(sql) { // 你可以模拟插入更多条数据,例如 1万+ var code, name, company_name, concepts, indudtry, province, city, introduce, created_at string type Column struct { Code string `json:"code"` Name string `json:"name"` Company_name string `json:"company_name"` Concepts string `json:"concepts"` Indudtry string `json:"indudtry"` Province string `json:"province"` City string `json:"city"` Introduce string `json:"introduce"` Created_at string `json:"created_at"` } for i := 1; i <= 500; i++ { var nColumn = make([]Column, 0) //2.执行批量查询 rows := t.QuerySqlForMultiple() if rows == nil { variable.ZapLog.Sugar().Error("sql执行失败,sql:", sql) return false } else { for rows.Next() { _ = rows.Scan(&code, &name, &company_name, &concepts, &indudtry, &province, &city, &introduce, &created_at) oneColumn := Column{ code, name, company_name, concepts, indudtry, province, city, introduce, created_at, } nColumn = append(nColumn, oneColumn) } //// 我们只输出最后一行数据 if i == 500 { fmt.Println("循环结束,最终需要返回的结果成员数量:",len(nColumn)) fmt.Printf("%#+v\n",nColumn) } } rows.Close() } } variable.ZapLog.Info("批量查询sql执行完毕!") return true } ``` -------------------------------- ### Updating Go Module Dependencies in go.mod Source: https://github.com/qifengzhang007/ginskeleton/blob/github/docs/faq.md This Go snippet demonstrates how to update a specific module's version in the `go.mod` file, using GORM as an example. After manually changing the version number (e.g., from `v1.20.5` to `v1.20.7`), the `go mod tidy` command must be executed in the terminal or the same directory as `go.mod` to synchronize dependencies and clean up unused ones. It's crucial to ensure the updated package version is compatible with the existing codebase. ```Go // 1. go.mod 文件修改以下版本号至最新版 gorm.io/gorm v1.20.5 ===> gorm.io/gorm v1.20.7 // 在goland终端或者 go.mod 同目录执行以下命令即可 go mod tidy ``` -------------------------------- ### User Registration Service with Password Encryption (Go) Source: https://github.com/qifengzhang007/ginskeleton/blob/github/docs/document.md This `UsersCurd` struct represents the service layer for user operations. The `Register` method preprocesses the user's password by encrypting it using MD5 before delegating the actual database storage to the `model` layer. This separates business logic (encryption) from data access. ```Go type UsersCurd struct { } // 预先处理密码加密,然后存储在数据库 func (u *UsersCurd) Register(userName, pass, userIp string) bool { pass = md5_encrypt.Base64Md5(pass) // 预先处理密码加密,然后存储在数据库 return model.CreateUserFactory("").Register(userName, pass, userIp) } ``` -------------------------------- ### Basic Inline Pre/Post Callbacks in Gin Controller Source: https://github.com/qifengzhang007/ginskeleton/blob/github/docs/aop.md This snippet shows a straightforward, but potentially polluting, way to implement pre- and post-operation callbacks directly within a Gin controller function. It demonstrates where 'before' logic (e.g., permission checks) and 'after' logic (e.g., data backup) would reside around the core business logic. ```Go func (u *Users) Destroy(context *gin.Context) { // before 删除之前回调代码... 例如:判断删除数据的用户是否具备相关权限等 userid := context.GetFloat64(consts.ValidatorPrefix + "id") // 根据 userid 执行删除用户数据(最核心代码) // after 删除之后回调代码... 例如 将删除的用户数据备份到相关的历史表 } ```