feat(auth): 添加完整的用户认证API项目

- 实现用户注册、登录、JWT令牌认证功能
- 集成Gin、GORM、Viper、Zap等框架
- 添加密码加密、数据库操作、中间件等完整功能
- 配置多环境支持、日志轮转、CORS处理
- 创建完整的项目结构和配置文件体系
This commit is contained in:
liumangmang
2025-12-30 18:00:42 +08:00
parent 7f4527d501
commit b010f82221
139 changed files with 2772 additions and 103 deletions

View File

@@ -0,0 +1,3 @@
module go-goroutine-gpm
go 1.22.2

View File

@@ -0,0 +1,76 @@
package main
import (
"fmt"
"runtime"
"sync"
"time"
)
func say(s string, id int) {
for i := 0; i < 3; i++ {
fmt.Printf("[%d] %s: step %d\n", id, s, i)
time.Sleep(100 * time.Millisecond) // 模拟工作
}
}
func main() {
fmt.Println("=== 1. 单线程顺序执行 ===")
say("Hello", 1)
say("World", 2)
fmt.Println("\n=== 2. 使用 Goroutine 并发执行 ===")
go say("Goroutine-A", 1)
go say("Goroutine-B", 2)
// 主 goroutine 等待 1 秒,否则程序会提前退出
time.Sleep(1 * time.Second)
fmt.Println("\n=== 3. 使用 sync.WaitGroup 安全等待 ===")
var wg sync.WaitGroup
for i := 1; i <= 3; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
fmt.Printf("Worker %d started\n", id)
time.Sleep(time.Duration(id*200) * time.Millisecond)
fmt.Printf("Worker %d finished\n", id)
}(i)
}
wg.Wait() // 阻塞直到所有 goroutine 完成
fmt.Println("All workers done!")
fmt.Println("\n=== 4. 查看当前 GOMAXPROCS 和 CPU 核心数 ===")
fmt.Printf("CPU 核心数: %d\n", runtime.NumCPU())
fmt.Printf("GOMAXPROCS: %d\n", runtime.GOMAXPROCS(0)) // 0 表示不修改,仅查询
fmt.Println("\n=== 5. 手动设置 GOMAXPROCS通常不需要===")
old := runtime.GOMAXPROCS(2)
fmt.Printf("旧 GOMAXPROCS: %d, 新设为: 2\n", old)
runtime.GOMAXPROCS(old) // 恢复原值
fmt.Println("\n=== 6. 观察 Goroutine 数量变化 ===")
fmt.Printf("启动前 Goroutine 数: %d\n", runtime.NumGoroutine())
var wg2 sync.WaitGroup
for i := 0; i < 5; i++ {
wg2.Add(1)
go func(n int) {
defer wg2.Done()
time.Sleep(200 * time.Millisecond)
}(i)
}
time.Sleep(50 * time.Millisecond) // 让 goroutine 启动
fmt.Printf("启动后 Goroutine 数: %d\n", runtime.NumGoroutine())
wg2.Wait()
fmt.Printf("全部完成后 Goroutine 数: %d\n", runtime.NumGoroutine())
// 在程序最后添加
time.Sleep(1 * time.Second)
fmt.Printf("延迟后 Goroutine 数: %d\n", runtime.NumGoroutine())
}

View File

@@ -0,0 +1,35 @@
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan int, 3) // 容量为 3 的有缓冲 channel
fmt.Println("[Main] 开始发送 3 个元素...")
ch <- 1
fmt.Println("[Main] 已发送 1")
ch <- 2
fmt.Println("[Main] 已发送 2")
ch <- 3
fmt.Println("[Main] 已发送 3 (已满)")
// 再发送一个会怎样?
go func() {
fmt.Println("[Sender] 尝试发送第 4 个元素(会阻塞,直到有接收者)...")
ch <- 4
fmt.Println("[Sender] 第 4 个元素发送成功")
}()
time.Sleep(1 * time.Second)
fmt.Println("[Main] 开始接收...")
for i := 0; i < 4; i++ {
v := <-ch
fmt.Println("[Main] 收到:", v)
}
fmt.Println("[Main] 程序结束")
}

View File

@@ -0,0 +1,28 @@
package main
import "fmt"
// 只负责发送数据
func producer(out chan<- int) {
for i := 1; i <= 5; i++ {
fmt.Println("[Producer] 发送:", i)
out <- i
}
fmt.Println("[Producer] 关闭 channel")
close(out) // 只有发送方才能关闭
}
// 只负责接收数据
func consumer(in <-chan int) {
for v := range in { // 直到 channel 被关闭
fmt.Println("[Consumer] 接收:", v)
}
fmt.Println("[Consumer] channel 已关闭,接收结束")
}
func main() {
ch := make(chan int)
go producer(ch) // ch 在这里被当作 只发送 channel 使用
consumer(ch) // ch 在这里被当作 只接收 channel 使用
}

View File

@@ -0,0 +1,3 @@
module go-channel-practice
go 1.22.2

View File

@@ -0,0 +1,37 @@
package main
import "fmt"
// 生产者:产生 1~5
func producer1(out chan<- int) {
for i := 1; i <= 5; i++ {
fmt.Println("[Producer] 发送:", i)
out <- i
}
close(out)
}
// 处理者:把数字放大 10 倍
func multiplier(in <-chan int, out chan<- int) {
for v := range in {
fmt.Println("[Multiplier] 接收:", v)
out <- v * 10
}
close(out)
}
// 消费者:打印结果
func consumer1(in <-chan int) {
for v := range in {
fmt.Println("[Consumer] 最终结果:", v)
}
}
func main() {
ch1 := make(chan int, 2) // 有缓冲,减轻 producer 阻塞
ch2 := make(chan int, 2)
go producer1(ch1)
go multiplier(ch1, ch2)
consumer1(ch2)
}

View File

@@ -0,0 +1,24 @@
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan string) // 无缓冲 channel
go func() {
fmt.Println("[Sender] 准备发送数据...")
ch <- "hello from goroutine" // 这里会阻塞,直到有人接收
fmt.Println("[Sender] 数据发送完毕")
}()
time.Sleep(1 * time.Second)
fmt.Println("[Main] 1 秒后开始接收数据...")
msg := <-ch // 接收数据,同时解除发送方阻塞
fmt.Println("[Main] 收到:", msg)
fmt.Println("[Main] 程序结束")
}

View File

@@ -0,0 +1,3 @@
module go-select-practice
go 1.22.2

View File

@@ -0,0 +1,33 @@
package main
import (
"fmt"
"time"
)
func main() {
ch1 := make(chan string)
ch2 := make(chan string)
// 模拟两个不同来源的“数据源”
go func() {
time.Sleep(1 * time.Second)
ch1 <- "result from ch1 (1s)"
}()
go func() {
time.Sleep(2 * time.Second)
ch2 <- "result from ch2 (2s)"
}()
fmt.Println("等待 ch1 或 ch2 的结果...(谁先来处理谁)")
select {
case v := <-ch1:
fmt.Println("收到 ch1:", v)
case v := <-ch2:
fmt.Println("收到 ch2:", v)
}
fmt.Println("main 结束")
}

View File

@@ -0,0 +1,38 @@
package main
import (
"context"
"fmt"
"time"
)
// 模拟一个可被取消的操作
func doWork(ctx context.Context) error {
for i := 1; i <= 5; i++ {
select {
case <-ctx.Done():
// context 被取消或超时
fmt.Println("doWork 被取消:", ctx.Err())
return ctx.Err()
default:
fmt.Println("工作中 step", i)
time.Sleep(1 * time.Second)
}
}
fmt.Println("doWork 正常完成")
return nil
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
fmt.Println("开始工作,最长 3 秒...")
if err := doWork(ctx); err != nil {
fmt.Println("结束,原因:", err)
return
}
fmt.Println("结束: 正常完成")
}

View File

@@ -0,0 +1,33 @@
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan int)
go func() {
for i := 1; i <= 5; i++ {
time.Sleep(500 * time.Millisecond)
ch <- i
}
close(ch)
}()
for {
select {
case v, ok := <-ch:
if !ok {
fmt.Println("channel 已关闭,退出循环")
return
}
fmt.Println("收到:", v)
default:
// 没有数据可读时,做点“其他事”
fmt.Println("没有新数据,先忙点别的...")
time.Sleep(200 * time.Millisecond)
}
}
}

View File

@@ -0,0 +1,46 @@
package main
import (
"errors"
"fmt"
"time"
)
// 模拟一个可能很慢的操作
func slowOperation() (string, error) {
time.Sleep(3 * time.Second) // 假设真的很慢
return "slow result", nil
}
func doWithTimeout(timeout time.Duration) (string, error) {
resultCh := make(chan string, 1)
errCh := make(chan error, 1)
go func() {
res, err := slowOperation()
if err != nil {
errCh <- err
return
}
resultCh <- res
}()
select {
case res := <-resultCh:
return res, nil
case err := <-errCh:
return "", err
case <-time.After(timeout):
return "", errors.New("操作超时")
}
}
func main() {
fmt.Println("开始调用,最大等待 2 秒...")
res, err := doWithTimeout(2 * time.Second)
if err != nil {
fmt.Println("失败:", err)
return
}
fmt.Println("成功:", res)
}

View File

@@ -0,0 +1,35 @@
package main
import (
"context"
"fmt"
"time"
)
// 模拟一个可被取消的循环任务
func worker(ctx context.Context, name string) {
for {
select {
case <-ctx.Done():
fmt.Println(name, "收到取消信号:", ctx.Err())
return
default:
fmt.Println(name, "还在干活...")
time.Sleep(500 * time.Millisecond)
}
}
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
go worker(ctx, "worker-1")
go worker(ctx, "worker-2")
time.Sleep(2 * time.Second)
fmt.Println("main: 决定取消所有 worker")
cancel() // 发出取消信号
time.Sleep(1 * time.Second)
fmt.Println("main 结束")
}

View File

@@ -0,0 +1,42 @@
package main
import (
"context"
"fmt"
"time"
)
func subTask(ctx context.Context, name string, d time.Duration) {
select {
case <-ctx.Done():
fmt.Println(name, "提前被取消:", ctx.Err())
case <-time.After(d):
fmt.Println(name, "完成,用时", d)
}
}
func mainTask(ctx context.Context) {
// 从上游 ctx 派生两个子 context
ctx1, cancel1 := context.WithCancel(ctx)
defer cancel1()
ctx2, cancel2 := context.WithTimeout(ctx, 2*time.Second)
defer cancel2()
go subTask(ctx1, "subTask-1", 5*time.Second)
go subTask(ctx2, "subTask-2", 5*time.Second)
time.Sleep(1 * time.Second)
fmt.Println("mainTask: 主动取消 subTask-1 的 ctx1")
cancel1()
// 等待一会儿,看 subTask-2 是否因超时被取消
time.Sleep(3 * time.Second)
}
func main() {
root := context.Background()
fmt.Println("开始 mainTask...")
mainTask(root)
fmt.Println("main 结束")
}

View File

@@ -0,0 +1,3 @@
module go-context-practice
go 1.22.2

View File

@@ -0,0 +1,37 @@
package main
import (
"context"
"fmt"
"time"
)
// 模拟一个可能很慢的操作
func slowJob(ctx context.Context) error {
for i := 1; i <= 5; i++ {
select {
case <-ctx.Done():
fmt.Println("slowJob 被取消:", ctx.Err())
return ctx.Err()
default:
fmt.Println("slowJob 进行中 step", i)
time.Sleep(1 * time.Second)
}
}
fmt.Println("slowJob 正常完成")
return nil
}
func main() {
// 最多给 slowJob 3 秒时间
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
fmt.Println("开始执行 slowJob超时时间 3 秒...")
if err := slowJob(ctx); err != nil {
fmt.Println("结束,原因:", err)
return
}
fmt.Println("结束:正常完成")
}

View File

@@ -0,0 +1,3 @@
module go-sync-practice
go 1.22.2

View File

@@ -0,0 +1,28 @@
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
var mu sync.Mutex
counter := 0
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for j := 0; j < 1000; j++ {
mu.Lock()
counter++
mu.Unlock()
}
}()
}
wg.Wait()
fmt.Println("期待的结果:", 1000*1000)
fmt.Println("实际结果:", counter)
}

View File

@@ -0,0 +1,25 @@
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
counter := 0
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for j := 0; j < 1000; j++ {
counter++
}
}()
}
wg.Wait()
fmt.Println("期待的结果:", 1000*1000)
fmt.Println("实际结果:", counter)
}

View File

@@ -0,0 +1,55 @@
package main
import (
"fmt"
"sync"
"time"
)
type Cache struct {
mu sync.RWMutex
data map[string]string
}
func (c *Cache) Get(key string) string {
c.mu.RLock()
defer c.mu.RUnlock()
return c.data[key]
}
func (c *Cache) Set(key, value string) {
c.mu.Lock()
defer c.mu.Unlock()
c.data[key] = value
}
func main() {
c := &Cache{data: make(map[string]string)}
c.Set("foo", "bar")
var wg sync.WaitGroup
// 多个读 goroutine
for i := 0; i < 5; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
for j := 0; j < 5; j++ {
v := c.Get("foo")
fmt.Printf("reader-%d 第 %d 次读到: %s\n", id, j, v)
time.Sleep(100 * time.Millisecond)
}
}(i)
}
// 一个写 goroutine
wg.Add(1)
go func() {
defer wg.Done()
time.Sleep(300 * time.Millisecond)
fmt.Println("writer: 更新 foo -> baz")
c.Set("foo", "baz")
}()
wg.Wait()
}

View File

@@ -0,0 +1,29 @@
package main
import (
"fmt"
"sync"
"time"
)
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done()
fmt.Printf("worker-%d 开始工作\n", id)
time.Sleep(time.Duration(id) * 300 * time.Millisecond)
fmt.Printf("worker-%d 完成\n", id)
}
func main() {
var wg sync.WaitGroup
n := 3
wg.Add(n)
for i := 1; i <= n; i++ {
go worker(i, &wg)
}
fmt.Println("main: 等待所有 worker 完成...")
wg.Wait()
fmt.Println("main: 全部完成")
}

View File

@@ -0,0 +1,16 @@
package main
import (
"fmt"
"sync/atomic"
)
func main() {
var value int64 = 0
success := atomic.CompareAndSwapInt64(&value, 0, 42)
fmt.Println("第一次 CAS 是否成功:", success, "当前值:", value)
success = atomic.CompareAndSwapInt64(&value, 0, 100)
fmt.Println("第二次 CAS 是否成功:", success, "当前值:", value)
}

View File

@@ -0,0 +1,26 @@
package main
import (
"fmt"
"sync"
"sync/atomic"
)
func main() {
var wg sync.WaitGroup
var counter int64 // 注意必须是 int64/uint64 等特定类型
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for j := 0; j < 1000; j++ {
atomic.AddInt64(&counter, 1)
}
}()
}
wg.Wait()
fmt.Println("期待的结果:", 1000*1000)
fmt.Println("实际结果:", counter)
}

View File

@@ -0,0 +1,24 @@
package main
import (
"fmt"
"sync/atomic"
"time"
)
func main() {
var stop int32 = 0
go func() {
for atomic.LoadInt32(&stop) == 0 {
// 忙等:什么也不干,不让出 CPU
}
fmt.Println("worker 退出")
}()
time.Sleep(2 * time.Second)
fmt.Println("main: 设置 stop=1")
atomic.StoreInt32(&stop, 1)
time.Sleep(1 * time.Second)
}

View File

@@ -0,0 +1,23 @@
package main
import (
"fmt"
"time"
)
func main() {
stop := make(chan struct{})
go func() {
select {
case <-stop:
fmt.Println("worker 收到停止信号,退出")
}
}()
time.Sleep(2 * time.Second)
fmt.Println("main: 关闭 stop channel")
close(stop)
time.Sleep(1 * time.Second)
}

View File

@@ -0,0 +1,3 @@
module go-atomic-cpu
go 1.22.2

View File

@@ -0,0 +1,81 @@
package main
import (
"fmt"
"io"
"net/http"
"regexp"
"sync"
)
func main() {
urls := []string{
"https://golang.org",
"https://go.dev",
"https://www.baidu.com",
"https://www.bing.com",
}
jobs := make(chan string)
results := make(chan string)
var wg sync.WaitGroup
workerCount := 3
//启动 worker
for i := 0; i < workerCount; i++ {
wg.Add(1)
go worker(i, jobs, results, &wg)
}
//发送任务
go func() {
for _, url := range urls {
jobs <- url
}
close(jobs) // 关闭 channel
}()
//单独goroutine 负责所有 worker结束后 关闭 results
go func() {
wg.Wait()
close(results)
}()
for res := range results {
fmt.Println(res)
}
}
// worker 从 jobs 读取 URL写结果到 results
func worker(id int, jobs <-chan string, results chan<- string, wg *sync.WaitGroup) {
defer wg.Done()
for url := range jobs {
title, err := fetchTitle(url)
if err != nil {
results <- fmt.Sprintf("[worker-%d] %s ERROR: %v", id, url, err)
continue
}
results <- fmt.Sprintf("[worker-%d] %s => %s", id, url, title)
}
}
func fetchTitle(url string) (string, error) {
resp, err := http.Get(url)
if err != nil {
return "", err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", err
}
re := regexp.MustCompile(`<title>(.+)</title>`)
matches := re.FindSubmatch(body)
if len(matches) >= 2 {
return string(matches[1]), nil
}
return "(no title)", nil
}

View File

@@ -0,0 +1,3 @@
module go-crawler
go 1.22.2