golang 包推荐
官方仓库:https://pkg.go.dev
示例源码仓库:https://gitee.com/orangbus/study-go
m3u8 解析库
go get -u github.com/grafov/m3u8
定时器
https://github.com/robfig/cron
go get -u github.com/robfig/cron/v3
func main() {
c := cron.New()
ch := make(chan int)
go func() {
c.AddFunc("@daily", func() {
time.Sleep(time.Second * 2)
fmt.Printf("%s\n", dns)
ch <- 1
})
}()
c.Start()
for {
data := <-ch
fmt.Println(data)
}
}
gin
go get -u github.com/gin-gonic/gin
goquery
go get -u github.com/PuerkitoBio/goquery
获取某个节点
func TestNode(t *testing.T) {
url := "https://www.hf960.com/n5061c23.aspx"
response, err := http.Get(url)
if err != nil {
panic(err)
}
defer response.Body.Close()
doc, err := goquery.NewDocumentFromReader(response.Body)
if err != nil {
panic(err)
}
selection := doc.Selection.Find("#bodyTd")
fmt.Println(selection.Find(".aTitle").Text())
fmt.Println(selection.Find("#content").Html())
}
获取列表
func spiderList(spider_url string) {
response, err := http.Get(spider_url)
if err != nil {
log.Printf("%s 请求错误:%s", spider_url, err.Error())
return
}
defer response.Body.Close()
if response.StatusCode != 200 {
log.Printf("请求错误,错误状态: %d", response.StatusCode)
return
}
doc, err := goquery.NewDocumentFromReader(response.Body)
if err != nil {
log.Printf("goquery解析错误:%s", err.Error())
return
}
var list []models.Articles
doc.Find(".ul13 li").Each(func(i int, s *goquery.Selection) {
var article models.Articles
article.Title = s.Find("a").Text()
article.Url = s.Find("a").AttrOr("href", "")
list = append(list, article)
log.Println(article)
})
}
Soup
https://github.com/anaskhan96/soup
go get github.com/anaskhan96/soup
package main
import (
"fmt"
"github.com/anaskhan96/soup"
"os"
)
func main() {
resp, err := soup.Get("https://xkcd.com")
if err != nil {
os.Exit(1)
}
doc := soup.HTMLParse(resp)
links := doc.Find("div", "id", "comicLinks").FindAll("a")
for _, link := range links {
fmt.Println(link.Text(), "| Link :", link.Attrs()["href"])
}
}
viper
https://github.com/spf13/viper
go get github.com/spf13/viper
go get github.com/spf13/cast // 类型转化
APP_NAME=orangbus
APP_KEY=zBqYyQrPNaIUsnRhsGtHLivjqiMjBVLS
APP_DEBUG=true
APP_URL=http://localhost:3000
APP_PORT=3000
/pkg/config/config.go
package config
import (
"github.com/spf13/cast"
viperlib "github.com/spf13/viper"
"orangbus.cn/study-go/index/pkg/helpers"
)
// Initialize 触发加载 config 包的所有 init 函数
func Initialize() {
}
var viper *viperlib.Viper
// ConfigFunc 动态加载配置信息
type ConfigFun func() map[string]interface{}
// ConfigFuncs 先加载到此数据, loadConfig 在动态生成配置信息
var ConfigFuncs map[string]ConfigFun
var configPath = ".env" // main.go 同目录下的 .env 文件
func init() {
viper = viperlib.New()
viper.SetConfigType("env")
viper.AddConfigPath(".")
viper.SetEnvPrefix("appenv")
// 读取环境变量
viper.AutomaticEnv()
ConfigFuncs = make(map[string]ConfigFun)
}
func InitConfig() {
// 加载环境变量
loadEnv()
// 注册配置信息
loadConfig()
}
func loadConfig() {
for name, fn := range ConfigFuncs {
viper.Set(name, fn())
}
}
func loadEnv() {
viper.SetConfigName(configPath)
if err := viper.ReadInConfig(); err != nil {
panic(err)
}
// 变更时重新加载
viper.WatchConfig()
}
func internalGet(path string, defaultValue ...interface{}) interface{} {
if !viper.IsSet(path) || helpers.Empty(viper.Get(path)) {
if len(defaultValue) > 0 {
return defaultValue[0]
}
return nil
}
return viper.Get(path)
}
// 读取环境变量,支持默认值
func Env(envName string, defaultValue ...interface{}) interface{} {
if len(defaultValue) > 0 {
return internalGet(envName, defaultValue[0])
}
return internalGet(envName)
}
func Add(name string, configFun ConfigFun) {
ConfigFuncs[name] = configFun
}
// GetString 获取 String 类型的配置信息
func GetString(path string, defaultValue ...interface{}) string {
return cast.ToString(internalGet(path, defaultValue...))
}
// GetInt 获取 Int 类型的配置信息
func GetInt(path string, defaultValue ...interface{}) int {
return cast.ToInt(internalGet(path, defaultValue...))
}
// GetBool 获取 Bool 类型的配置信息
func GetBool(path string, defaultValue ...interface{}) bool {
return cast.ToBool(internalGet(path, defaultValue...))
}
// GetStringMapString 获取结构数据
func GetStringMapString(path string) map[string]string {
return viper.GetStringMapString(path)
}
/pkg/helpers/helpers.go
// Package helpers 存放辅助方法
package helpers
import "reflect"
// Empty 类似于 PHP 的 empty() 函数
func Empty(val interface{}) bool {
if val == nil {
return true
}
v := reflect.ValueOf(val)
switch v.Kind() {
case reflect.String, reflect.Array:
return v.Len() == 0
case reflect.Map, reflect.Slice:
return v.Len() == 0 || v.IsNil()
case reflect.Bool:
return !v.Bool()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return v.Int() == 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return v.Uint() == 0
case reflect.Float32, reflect.Float64:
return v.Float() == 0
case reflect.Interface, reflect.Ptr:
return v.IsNil()
}
return reflect.DeepEqual(val, reflect.Zero(v.Type()).Interface())
}
/config/app.go
package config
import "orangbus.cn/study-go/index/pkg/config"
func init() {
config.Add("app", func() map[string]interface{} {
return map[string]interface{}{
"name": config.Env("APP_NAME", "orangbus"),
"key": config.Env("APP_KEY", "33446a9dcf9ea060a0a6532b166da32f304af0de"),
"debug": config.Env("APP_DEBUG", false),
"port": config.Env("APP_PORT", 3000),
"url": config.Env("APP_URL", "http://localhost"),
}
})
}
main.go
package main
import (
"fmt"
"orangbus.cn/study-go/index/pkg/config"
)
func init() {
config.Initialize()
config.InitConfig()
}
func main() {
fmt.Println(config.GetInt("app_port"))
fmt.Println(config.GetInt("APP_PORT"))
}
示例2
gorm
https://gorm.io/zh_CN/docs/index.html
go get gorm.io/gorm
go get gorm.io/driver/sqlite
go get gorm.io/driver/mysql
1、初始化
bootstrap/database.go
package bootstrap
import (
"database-gorm/pkg/database"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"os"
"path/filepath"
)
func SetupDatabase() {
var dbConfig gorm.Dialector
dbConfig = sqlite.Open(getDatabasePath())
database.Connect(dbConfig)
}
func getDatabasePath() string {
basePath, err := os.Getwd()
if err != nil {
return "database/database.sqlite"
}
return filepath.Join(basePath, "database/database.sqlite")
}
2、连接数据库
/pkg/database/database.go
package database
import (
"database/sql"
"gorm.io/gorm"
"time"
)
var (
DB *gorm.DB
SqlDB *sql.DB
)
/*
*
连接数据库
*/
func Connect(dbConfig gorm.Dialector) {
var err error
DB, err = gorm.Open(dbConfig, &gorm.Config{
CreateBatchSize: 500,
})
if err != nil {
panic(err)
}
SqlDB, err = DB.DB()
SqlDB.SetMaxIdleConns(10)
SqlDB.SetMaxOpenConns(10)
SqlDB.SetConnMaxLifetime(time.Hour)
if err != nil {
panic(err)
}
}
faker
https://pkg.go.dev/github.com/bxcodec/faker/v3
go get -u github.com/bxcodec/faker/v3
package factories
import (
"database-gorm/app/models"
"database-gorm/pkg/database"
"github.com/bxcodec/faker/v3"
)
func FakerUser(number int) {
userList := []models.User{}
for i := 0; i < number; i++ {
user := models.User{
Name: faker.Name(),
Phone: faker.Phonenumber(),
Password: faker.Password(),
}
userList = append(userList, user)
}
// 插入到数据库中
database.DB.CreateInBatches(userList, 500)
}
uuid
go get -u github.com/google/uuid
package test
import (
"fmt"
"github.com/google/uuid"
"testing"
)
func TestUUid(t *testing.T) {
fmt.Println(uuid.NewString()) // 6749b706-bb35-4760-9a23-f3ef65101e6c
}
cron
go get -u github.com/robfig/cron/v3
0,15,30,45 8-23 * * *
0,15,30,45 分钟字段,表示每小时的第0分钟、第15分钟、第30分钟和第45分钟。
8-23 小时字段,表示从早上8点到晚上23点。
* 日期字段,表示每天。
* 月份字段,表示每个月。
* 星期字段,表示星期的每一天。
package bootstrap
import (
"github.com/robfig/cron/v3"
"log"
"movie-cloud/app/service/movie_service"
)
func NewSchedule() {
go start()
}
func start() {
c := cron.New()
// 每日定时检查接口
c.AddFunc("@daily", func() {
movie_service.CheckMovieApiIsOk()
log.Panicln("接口检查完毕")
})
c.Start()
}
cobra命令行工具
地址:
安装脚手架
go install github.com/spf13/cobra-cli@latest
# 测试
cobra-cli
安装依赖
go get -u github.com/spf13/cobra@latest
初始化一个项目
go mod init app
cobra-cli init
添加一个命令
cobra-cli add version # 随机给我生成一个字符串出来
package cmd
import (
"fmt"
"github.com/spf13/cobra"
)
var versionCmd = &cobra.Command{
Use: "version",
Short: "显示版本信息",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("v1.1.0")
},
}
func init() {
rootCmd.AddCommand(versionCmd)
}
bcrypt 加密
go get golang.org/x/crypto/bcrypt
package test
import (
"fmt"
"golang.org/x/crypto/bcrypt"
"testing"
)
var password = "admin666"
// 加密
func TestBcrypt(t *testing.T) {
passwd, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
fmt.Println(passwd)
return
}
fmt.Println(string(passwd)) // $2a$10$zVPrJ.kiAWKqEKhuYjSpAu9QAjj/Mp7Mnn5P5dRRO8IvMqMkEzXR.
}
// 密码比对
func TestComparePwd(t *testing.T) {
hashPasswd := "$2a$10$zVPrJ.kiAWKqEKhuYjSpAu9QAjj/Mp7Mnn5P5dRRO8IvMqMkEzXR."
err := bcrypt.CompareHashAndPassword([]byte(hashPasswd), []byte(password))
if err != nil {
fmt.Println(err)
return
}
fmt.Println("密码相同")
}
jwt
https://github.com/dgrijalva/jwt-go
go get github.com/dgrijalva/jwt-go
pkg/jwtToken/jwtToken
package jwtToken
import (
"gin-web/app/models"
"github.com/dgrijalva/jwt-go"
"time"
)
const app_key = "orangbus.cn"
const expire_at = 30 // 失效天数
func GenerateToken(id int64,scope string) (string, error) {
claims := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"id": id,
"scope": scope,
"exp": time.Now().Add(time.Hour * 24 * expire_at).Unix(),
"iat": time.Now().Unix(), // 发布时间
})
token, err := claims.SignedString([]byte(app_key))
if err != nil {
return "", err
}
return token, nil
}
func Parse(token string,scope string) (int64, error) {
user := models.User{}
parse, err := jwt.Parse(token, func(token *jwt.Token) (interface{}, error) {
return []byte(app_key), nil
})
if err != nil {
return models.User{}, err
}
if claims, ok := parse.Claims.(jwt.MapClaims); ok && parse.Valid {
id = claims["id"].(float64)
tokenScope = claims["scope"].(string)
if (scope != tokenScope){
return 0,errors.New("scope 校验失败")
}
}
return id, nil
}
webscoket
go-redis
go get github.com/redis/go-redis/v9
pkg/cache/redis
package cache
import (
"context"
"github.com/redis/go-redis/v9"
"movie-cloud/pkg/config"
"runtime"
"time"
)
var (
Redis *redis.Client
prefix = "movie_cloud_"
ctx = context.Background()
)
/*
*
连接数据库
*/
func ConnectRedis() {
Redis = redis.NewClient(&redis.Options{
Addr: config.GetRedisHost(),
Password: config.GetRedisPassword(),
DB: config.GetRedisDB(),
PoolSize: runtime.GOMAXPROCS(100),
// 最小空闲连接数,受PoolSize限制
MinIdleConns: 10,
})
}
func Put(key, value string, duration ...int) {
t := time.Duration(0)
if len(duration) == 0 {
t = time.Duration(duration[0])
}
Redis.Set(ctx, prefix+key, value, t)
}
func Get(key string) (string, error) {
return Redis.Get(ctx, prefix+key).Result()
}
ip2region
https://github.com/lionsoul2014/ip2region?tab=readme-ov-file
https://github.com/lionsoul2014/ip2region/tree/master/binding/golang
package service
import (
"github.com/lionsoul2014/ip2region/binding/golang/xdb"
"strings"
)
var dbPath = "ip2region.xdb"
func GetIpInfo(ip string) (map[string]string, error) {
searcher, err := xdb.NewWithFileOnly(dbPath)
return nil, err
defer searcher.Close()
ipInfo, err := searcher.SearchByStr(ip)
return nil, err
info := strings.Split(ipInfo, "|")
region := map[string]string{}
region["ip"] = ip
region["county"] = info[0]
region["area"] = info[1]
region["region"] = info[2]
region["city"] = info[3]
region["isp"] = info[4]
return region, nil
}
markdown 解析
https://github.com/russross/blackfriday/v2
func GetReadme() (string, error) {
file, err := os.Open(readmePath)
if err != nil {
return "", err
}
defer file.Close()
bytes, err := io.ReadAll(file)
if err != nil {
return "", err
}
content := blackfriday.Run(bytes)
return string(content), nil
}
mapstructure
结构体绑定
go get github.com/mitchellh/mapstructure
package test
import (
"github.com/mitchellh/mapstructure"
"testing"
)
type User struct {
Name string `json:"name"`
Age int `json:"age"`
Phone string `json:"phone"`
}
type ReqUser struct {
Name string `json:"name"`
Phone string `json:"phone"`
}
// 请请求的参数绑定到结构体上
func TestBind(t *testing.T) {
param := ReqUser{
Name: "orangbus+1",
Phone: "18388122501+1",
}
user := User{
Name: "orangbus",
Age: 18,
Phone: "18388123501",
}
// 不会覆盖已有的参数,只会将 有的参数绑定到结构体上
err := mapstructure.Decode(param, &user)
if err != nil {
t.Log(err)
return
}
t.Log(user) // {orangbus+1 18 18388122501+1}
}
carbon 时间
https://github.com/golang-module/carbon
go get -u github.com/golang-module/carbon/v2
carbon.SetDefault(carbon.Default{
Layout: carbon.DateTimeLayout,
Timezone: carbon.Local,
WeekStartsAt: carbon.Sunday,
Locale: "zh",
})
自定义时间格式解析
// dateStr : "04月08日"
func parseDate(dateStr string) string {
// 获取当前年份
currentYear := time.Now().Year()
// 定义完整的日期格式,包括年份
fullDateStr := fmt.Sprintf("%04d年%s", currentYear, dateStr)
// 定义日期格式
dateLayout := "2006年01月02日"
// 解析日期
parsedDate, err := time.ParseInLocation(dateLayout, fullDateStr, time.Local)
if err != nil {
fmt.Println("Error parsing date:", err)
return ""
}
return parsedDate.Format("2006-01-02 15:04:05") // 2024-04-08 00:00:00
}
air
go install github.com/cosmtrek/air@latest
air -v
webscoket
go get -u github.com/gorilla/websocket
前端连接
https://www.npmjs.com/package/vue3-websocket
npm i vue3-websocket
main.js
import { createApp } from 'vue'
import App from './App.vue'
import socket from 'vue3-websocket'
const app = createApp(App)
// app.use(socket, 'ws://localhost:9000')
app.use(socket, {
secured: false,
host: 'localhost:9000/ws',
protocols: ['soap']
})
app.mount('#app')
vue-templet
<template>
<input v-model="text" />
<button @click="sendMessage">Send a message</button>
</template>
<script setup>
import { ref, inject } from 'vue'
import { onMessage, onOpen, onClose, onError } from 'vue3-websocket'
const text = ref('')
const socket = inject('socket')
const sendMessage = () => socket.value.send(text.value)
//
onOpen(() => {
console.log('WS connection is stable! ~uWu~')
})
onMessage(message => {
console.log('Got a message from the WS: ', message)
})
onClose(() => {
console.log('No way, connection has been closed 😥')
})
onError(error => {
console.error('Error: ', error)
})
</script>
MongoDB
go get -u go.mongodb.org/mongo-driver
发送邮箱
pdf转化
调用系统命令
package main
import (
"fmt"
"os"
"os/exec"
)
func main() {
cmd := exec.Command("ls", "-l")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
fmt.Println(err)
return
}
}
yaml
go get gopkg.in/yaml.v2
修改配置文件
package main
import (
"fmt"
"io/ioutil"
"gopkg.in/yaml.v2"
)
type Config struct {
Key1 string `yaml:"key1"`
Key2 string `yaml:"key2"`
// 添加其他键值对...
}
func main() {
// 读取 YAML 文件
data, err := ioutil.ReadFile("config.yaml")
if err != nil {
fmt.Println("无法读取文件:", err)
return
}
// 解析 YAML 文件到结构体
var config Config
err = yaml.Unmarshal(data, &config)
if err != nil {
fmt.Println("解析 YAML 失败:", err)
return
}
// 修改值
config.Key1 = "new_value"
// 将更改后的结构体写回 YAML 文件
newData, err := yaml.Marshal(&config)
if err != nil {
fmt.Println("转换 YAML 失败:", err)
return
}
err = ioutil.WriteFile("config.yaml", newData, 0644)
if err != nil {
fmt.Println("写回文件失败:", err)
return
}
fmt.Println("已成功修改 YAML 文件中的值。")
}
fatih/color
https://github.com/fatih/color
go get github.com/fatih/color
excel
go get -u github.com/tealeg/xlsx
package main
import (
"fmt"
"github.com/tealeg/xlsx"
)
type User struct {
Sex string `json:"sex"`
IDCard string `json:"id_card"`
Grade string `json:"grade"`
}
func ReadExcel(filename string) ([]User, error) {
var list []User
file, err := xlsx.OpenFile(filename)
if err != nil {
return list, err
}
for key, sheel := range file.Sheets {
if key == 0 {
// 读取行
for _, row := range sheel.Rows {
// 遍历一行的字段
var user User
user.Sex = row.Cells[0].Value
user.IDCard = row.Cells[1].Value
user.Grade = row.Cells[2].Value
list = append(list, user)
}
}
}
return list, err
}
func WriteExcel(filename string, header []string, users []User) {
file := xlsx.NewFile()
sheet, err := file.AddSheet("Sheel1")
if err != nil {
fmt.Println(err)
return
}
total := len(header)
hd := sheet.AddRow()
for i := 0; i < total; i++ {
cell := hd.AddCell()
cell.Value = header[i]
}
// 添加内容
for startIndex, user := range users {
if startIndex > 0 { // 忽略第一行
for i := 0; i < total; i++ {
newRow := sheet.AddRow()
cell := newRow.AddCell()
cell.Value = user.Sex
cell = newRow.AddCell()
cell.Value = user.IDCard
cell = newRow.AddCell()
cell.Value = user.Grade
}
}
}
err = file.Save(filename)
if err != nil {
fmt.Println(err)
return
}
fmt.Println("ok")
}
func main() {
users, err := ReadExcel("demo.xlsx")
if err != nil {
return
}
for _, user := range users {
fmt.Printf("%v\n", user)
}
header := []string{"性别", "身份证号", "报考专业"}
WriteExcel("ok.xlsx", header, users)
}
ollama
开启远程访问,编辑这个文件,追加这两个配置即可
sudo vim /etc/systemd/system/ollama.service
[Service]
Environment="OLLAMA_HOST=0.0.0.0" # 所有地址可访问
Environment="OLLAMA_ORIGINS=*" # 允许跨域
package test
import (
"bufio"
bytes2 "bytes"
"encoding/json"
"net/http"
"testing"
)
var api_url = "http://home.cc:11434/api/generate"
type ParamGenerate struct {
Model string `json:"model"`
Prompt string `json:"prompt"`
}
func TestGenerate(t *testing.T) {
body := ParamGenerate{
Model: "llama3",
Prompt: "你是谁",
}
marshal, err := json.Marshal(body)
if err != nil {
return
}
buffer := bytes2.NewBuffer(marshal)
response, err := http.Post(api_url, "application/json", buffer)
if err != nil {
t.Log(err)
return
}
defer response.Body.Close()
ch := make(chan int, 1)
go func(ch chan int) {
scanner := bufio.NewScanner(response.Body)
for scanner.Scan() {
line := scanner.Text()
t.Log(line)
}
ch <- 1
}(ch)
<-ch
t.Log("done!")
close(ch)
}
type ParamChat struct {
Model string `json:"model"`
Messages []ChatMessage `json:"messages"`
Stream bool `json:"stream"`
}
type ChatMessage struct {
Role string `json:"role"`
Content string `json:"content"`
}
func TestChat(t *testing.T) {
var chat []ChatMessage
var msg ChatMessage
msg.Role = "user"
msg.Content = "你是谁?"
chat = append(chat, msg)
body := ParamChat{
Model: "llama3",
Stream: true,
Messages: chat,
}
marshal, err := json.Marshal(body)
if err != nil {
return
}
buffer := bytes2.NewBuffer(marshal)
response, err := http.Post("http://home.cc:11434/api/chat", "application/json", buffer)
if err != nil {
t.Log(err)
return
}
defer response.Body.Close()
ch := make(chan int, 1)
go func(ch chan int) {
scanner := bufio.NewScanner(response.Body)
for scanner.Scan() {
line := scanner.Text()
// 解析返回的数据
t.Log(line)
}
ch <- 1
}(ch)
<-ch
t.Log("done!")
close(ch)
}