feat: PhotoWall 毕业照存储系统初始版本
后端: Go(Gin+GORM+MySQL+Redis) - 17张业务表(yz_pw_前缀), 自动迁移 - JWT认证(3小时) + 盐+MD5密码 + 图形验证码 - 班级CRUD/加入(8人姓名验证/邀请码)/审核/30天自动清理 - 系统配置(16项)/菜单管理(动态路由)/数据统计 - 文件MD5去重/数据隔离/账号封禁/敏感词DFA检测 前端: Vue3+Vite+Element Plus+Less+ECharts+FontAwesome - 动态路由(数据库菜单驱动) - 蓝白配色, H5响应式 - 登录/注册/忘记密码/个人中心 - 班级创建向导/详情/加入/列表 - 管理后台: 审核/配置/菜单/统计/用户/敏感词 数据库: MySQL 10.31.100.3:3306/photowall 管理员: hero920103 / 920103
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
package redis
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
var (
|
||||
Client *redis.Client
|
||||
Enabled bool
|
||||
memoryStore sync.Map // Redis 不可用时的内存降级存储
|
||||
)
|
||||
|
||||
// Config Redis 配置
|
||||
type Config struct {
|
||||
Host string
|
||||
Port string
|
||||
Password string
|
||||
DB int
|
||||
}
|
||||
|
||||
// Init 初始化 Redis 连接,失败则降级为内存模式
|
||||
func Init(cfg Config) {
|
||||
Client = redis.NewClient(&redis.Options{
|
||||
Addr: fmt.Sprintf("%s:%s", cfg.Host, cfg.Port),
|
||||
Password: cfg.Password,
|
||||
DB: cfg.DB,
|
||||
})
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if err := Client.Ping(ctx).Err(); err != nil {
|
||||
log.Printf("[redis] connection failed (%v), fallback to memory mode", err)
|
||||
Enabled = false
|
||||
Client = nil
|
||||
return
|
||||
}
|
||||
|
||||
Enabled = true
|
||||
log.Printf("[redis] connected to %s:%s (db=%d)", cfg.Host, cfg.Port, cfg.DB)
|
||||
}
|
||||
|
||||
// Set 设置键值(带过期时间,0=不过期)
|
||||
func Set(ctx context.Context, key string, value interface{}, expiration time.Duration) error {
|
||||
if Enabled && Client != nil {
|
||||
return Client.Set(ctx, key, value, expiration).Err()
|
||||
}
|
||||
// 内存降级
|
||||
memoryStore.Store(key, memoryItem{value: value, expireAt: time.Now().Add(expiration)})
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get 获取值
|
||||
func Get(ctx context.Context, key string) (string, error) {
|
||||
if Enabled && Client != nil {
|
||||
return Client.Get(ctx, key).Result()
|
||||
}
|
||||
// 内存降级
|
||||
v, ok := memoryStore.Load(key)
|
||||
if !ok {
|
||||
return "", redis.Nil
|
||||
}
|
||||
item := v.(memoryItem)
|
||||
if time.Now().After(item.expireAt) {
|
||||
memoryStore.Delete(key)
|
||||
return "", redis.Nil
|
||||
}
|
||||
return fmt.Sprintf("%v", item.value), nil
|
||||
}
|
||||
|
||||
// Del 删除键
|
||||
func Del(ctx context.Context, key string) error {
|
||||
if Enabled && Client != nil {
|
||||
return Client.Del(ctx, key).Err()
|
||||
}
|
||||
memoryStore.Delete(key)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Exists 检查键是否存在
|
||||
func Exists(ctx context.Context, key string) (int64, error) {
|
||||
if Enabled && Client != nil {
|
||||
return Client.Exists(ctx, key).Result()
|
||||
}
|
||||
_, ok := memoryStore.Load(key)
|
||||
if ok {
|
||||
return 1, nil
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// Incr 自增
|
||||
func Incr(ctx context.Context, key string) (int64, error) {
|
||||
if Enabled && Client != nil {
|
||||
return Client.Incr(ctx, key).Result()
|
||||
}
|
||||
// 内存降级(简单实现)
|
||||
v, _ := Get(ctx, key)
|
||||
var n int64
|
||||
fmt.Sscanf(v, "%d", &n)
|
||||
n++
|
||||
Set(ctx, key, n, 0)
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// memoryItem 内存存储项
|
||||
type memoryItem struct {
|
||||
value interface{}
|
||||
expireAt time.Time
|
||||
}
|
||||
Reference in New Issue
Block a user