feat: 实现公共工具包,包括日志、错误处理、数据库和缓存

This commit is contained in:
fish
2026-03-28 20:00:43 +08:00
parent be24b465b1
commit d0dbc4a3a7
5 changed files with 388 additions and 0 deletions

120
backend/shared/pkg/cache/redis.go vendored Normal file
View File

@@ -0,0 +1,120 @@
package cache
import (
"context"
"time"
"backend/shared/pkg/logger"
"github.com/go-redis/redis/v8"
)
type RedisConfig struct {
Addr string
Password string
DB int
}
type RedisCache struct {
Client *redis.Client
ctx context.Context
}
// NewRedisCache 创建新的 Redis 缓存连接
func NewRedisCache(config RedisConfig) (*RedisCache, error) {
client := redis.NewClient(&redis.Options{
Addr: config.Addr,
Password: config.Password,
DB: config.DB,
})
ctx := context.Background()
// 测试连接
if _, err := client.Ping(ctx).Result(); err != nil {
return nil, err
}
logger.Info("Connected to Redis cache")
return &RedisCache{
Client: client,
ctx: ctx,
}, nil
}
// Close 关闭 Redis 连接
func (r *RedisCache) Close() error {
if r.Client != nil {
return r.Client.Close()
}
return nil
}
// Set 设置缓存
func (r *RedisCache) Set(key string, value interface{}, expiration time.Duration) error {
return r.Client.Set(r.ctx, key, value, expiration).Err()
}
// Get 获取缓存
func (r *RedisCache) Get(key string) (string, error) {
return r.Client.Get(r.ctx, key).Result()
}
// Delete 删除缓存
func (r *RedisCache) Delete(key string) error {
return r.Client.Del(r.ctx, key).Err()
}
// Exists 检查键是否存在
func (r *RedisCache) Exists(key string) (bool, error) {
result, err := r.Client.Exists(r.ctx, key).Result()
if err != nil {
return false, err
}
return result > 0, nil
}
// Expire 设置键的过期时间
func (r *RedisCache) Expire(key string, expiration time.Duration) error {
return r.Client.Expire(r.ctx, key, expiration).Err()
}
// TTL 获取键的剩余过期时间
func (r *RedisCache) TTL(key string) (time.Duration, error) {
return r.Client.TTL(r.ctx, key).Result()
}
// Incr 递增键的值
func (r *RedisCache) Incr(key string) (int64, error) {
return r.Client.Incr(r.ctx, key).Result()
}
// Decr 递减键的值
func (r *RedisCache) Decr(key string) (int64, error) {
return r.Client.Decr(r.ctx, key).Result()
}
// HashSet 设置哈希表字段
func (r *RedisCache) HashSet(key, field string, value interface{}) error {
return r.Client.HSet(r.ctx, key, field, value).Err()
}
// HashGet 获取哈希表字段
func (r *RedisCache) HashGet(key, field string) (string, error) {
return r.Client.HGet(r.ctx, key, field).Result()
}
// HashDelete 删除哈希表字段
func (r *RedisCache) HashDelete(key string, fields ...string) error {
return r.Client.HDel(r.ctx, key, fields...).Err()
}
// HashExists 检查哈希表字段是否存在
func (r *RedisCache) HashExists(key, field string) (bool, error) {
result, err := r.Client.HExists(r.ctx, key, field).Result()
if err != nil {
return false, err
}
return result, nil
}

View File

@@ -0,0 +1,79 @@
package database
import (
"database/sql"
"fmt"
"time"
"backend/shared/pkg/logger"
_ "github.com/lib/pq"
)
type PostgresConfig struct {
Host string
Port int
User string
Password string
DBName string
SSLMode string
}
type PostgresDB struct {
DB *sql.DB
}
// NewPostgresDB 创建新的 PostgreSQL 数据库连接
func NewPostgresDB(config PostgresConfig) (*PostgresDB, error) {
connStr := fmt.Sprintf(
"host=%s port=%d user=%s password=%s dbname=%s sslmode=%s",
config.Host, config.Port, config.User, config.Password, config.DBName, config.SSLMode,
)
db, err := sql.Open("postgres", connStr)
if err != nil {
return nil, err
}
// 设置连接池参数
db.SetMaxOpenConns(25)
db.SetMaxIdleConns(5)
db.SetConnMaxLifetime(5 * time.Minute)
// 测试连接
if err := db.Ping(); err != nil {
return nil, err
}
logger.Info("Connected to PostgreSQL database")
return &PostgresDB{DB: db}, nil
}
// Close 关闭数据库连接
func (p *PostgresDB) Close() error {
if p.DB != nil {
return p.DB.Close()
}
return nil
}
// Exec 执行 SQL 语句
func (p *PostgresDB) Exec(query string, args ...interface{}) (sql.Result, error) {
return p.DB.Exec(query, args...)
}
// Query 执行查询
func (p *PostgresDB) Query(query string, args ...interface{}) (*sql.Rows, error) {
return p.DB.Query(query, args...)
}
// QueryRow 执行单行查询
func (p *PostgresDB) QueryRow(query string, args ...interface{}) *sql.Row {
return p.DB.QueryRow(query, args...)
}
// Begin 开始事务
func (p *PostgresDB) Begin() (*sql.Tx, error) {
return p.DB.Begin()
}

View File

@@ -0,0 +1,96 @@
package errors
import (
"errors"
"fmt"
)
// 错误类型定义
var (
ErrNotFound = errors.New("resource not found")
ErrInvalidInput = errors.New("invalid input")
ErrInternalServer = errors.New("internal server error")
ErrUnauthorized = errors.New("unauthorized")
ErrForbidden = errors.New("forbidden")
ErrConflict = errors.New("conflict")
ErrBadRequest = errors.New("bad request")
ErrTimeout = errors.New("timeout")
ErrServiceUnavailable = errors.New("service unavailable")
)
// AppError 应用错误结构
type AppError struct {
Err error
Message string
Code int
}
func (e *AppError) Error() string {
if e.Message != "" {
return e.Message
}
return e.Err.Error()
}
func (e *AppError) Unwrap() error {
return e.Err
}
// NewAppError 创建新的应用错误
func NewAppError(err error, message string, code int) *AppError {
return &AppError{
Err: err,
Message: message,
Code: code,
}
}
// WrapError 包装错误
func WrapError(err error, message string) error {
return fmt.Errorf("%s: %w", message, err)
}
// IsNotFound 检查是否为资源未找到错误
func IsNotFound(err error) bool {
return errors.Is(err, ErrNotFound)
}
// IsInvalidInput 检查是否为无效输入错误
func IsInvalidInput(err error) bool {
return errors.Is(err, ErrInvalidInput)
}
// IsInternalServer 检查是否为内部服务器错误
func IsInternalServer(err error) bool {
return errors.Is(err, ErrInternalServer)
}
// IsUnauthorized 检查是否为未授权错误
func IsUnauthorized(err error) bool {
return errors.Is(err, ErrUnauthorized)
}
// IsForbidden 检查是否为禁止访问错误
func IsForbidden(err error) bool {
return errors.Is(err, ErrForbidden)
}
// IsConflict 检查是否为冲突错误
func IsConflict(err error) bool {
return errors.Is(err, ErrConflict)
}
// IsBadRequest 检查是否为请求错误
func IsBadRequest(err error) bool {
return errors.Is(err, ErrBadRequest)
}
// IsTimeout 检查是否为超时错误
func IsTimeout(err error) bool {
return errors.Is(err, ErrTimeout)
}
// IsServiceUnavailable 检查是否为服务不可用错误
func IsServiceUnavailable(err error) bool {
return errors.Is(err, ErrServiceUnavailable)
}

View File

@@ -0,0 +1,38 @@
package logger
import (
"log"
"os"
)
type Logger struct {
infoLogger *log.Logger
errorLogger *log.Logger
debugLogger *log.Logger
}
var instance *Logger
func init() {
instance = NewLogger()
}
func NewLogger() *Logger {
return &Logger{
infoLogger: log.New(os.Stdout, "INFO: ", log.Ldate|log.Ltime|log.Lshortfile),
errorLogger: log.New(os.Stderr, "ERROR: ", log.Ldate|log.Ltime|log.Lshortfile),
debugLogger: log.New(os.Stdout, "DEBUG: ", log.Ldate|log.Ltime|log.Lshortfile),
}
}
func Info(format string, v ...interface{}) {
instance.infoLogger.Printf(format, v...)
}
func Error(format string, v ...interface{}) {
instance.errorLogger.Printf(format, v...)
}
func Debug(format string, v ...interface{}) {
instance.debugLogger.Printf(format, v...)
}