refactor: improve system stability, performance, and code structure
All checks were successful
Build Backend / build (push) Successful in 3m2s
Build Backend / build-docker (push) Successful in 2m44s

This commit introduces several architectural improvements and optimizations across the codebase:

- **Performance & Reliability**:
  - Implemented Redis pipelining in `ConversationCache.CacheMessage` to reduce network round-trips.
  - Added a circuit breaker to the JPush client to prevent cascading failures.
  - Introduced batch deletion and batch member addition capabilities in repositories.
  - Added message idempotency support using `client_msg_id` and a Redis-based cache.
  - Optimized WebSocket handling with connection limits (total and per-user) and improved error logging.

- **Code Refactoring**:
  - Refactored `Router` to use a `RouterDeps` struct, simplifying the constructor and improving maintainability.
  - Unified model ID generation logic using new `id_helper.go` (supporting UUID and Snowflake).
  - Standardized JSON serialization/deserialization in models using `json_helper.go`.
  - Refactored DTO conversion logic, specifically for `UserResponse` (using functional options) and `Report` responses.
  - Removed redundant/deprecated DTOs like `PostDetailResponse` and `TradeItemDetailResponse`.

- **Cache Improvements**:
  - Enhanced `LayeredCache` with `SetRaw` to avoid double-encoding when promoting values from Redis to local cache.
  - Added `DeleteBatch` support to the cache interface.

- **Other Changes**:
  - Cleaned up `config.go` by removing redundant default values and explicit environment variable overrides.
  - Improved WebSocket registration flow to handle connection limits gracefully.
This commit is contained in:
2026-05-04 13:07:03 +08:00
parent b2b55ea52d
commit ee78071d4d
65 changed files with 1293 additions and 975 deletions

View File

@@ -6,6 +6,8 @@ import (
"fmt"
"time"
"github.com/redis/go-redis/v9"
"with_you/internal/model"
"go.uber.org/zap"
@@ -618,11 +620,11 @@ func (c *ConversationCache) GetMessagesBeforeSeq(ctx context.Context, convID str
return messages, nil
}
// CacheMessage 缓存单条消息(立即写入缓存
// 写入 Hash、Sorted Set、更新计数
// CacheMessage 缓存单条消息(使用 Redis Pipeline 减少网络往返
func (c *ConversationCache) CacheMessage(ctx context.Context, convID string, msg *model.Message) error {
hashKey := MessageHashKey(convID)
indexKey := MessageIndexKey(convID)
countKey := MessageCountKey(convID)
msgData := MessageCacheDataFromModel(msg)
data, err := json.Marshal(msgData)
@@ -630,23 +632,34 @@ func (c *ConversationCache) CacheMessage(ctx context.Context, convID string, msg
return fmt.Errorf("failed to marshal message: %w", err)
}
// HSET 消息详情
if err := c.cache.HSet(ctx, hashKey, fmt.Sprintf("%d", msg.Seq), string(data)); err != nil {
hashKey = ResolveKey(hashKey)
indexKey = ResolveKey(indexKey)
countKey = ResolveKey(countKey)
redisCache, ok := c.cache.(*RedisCache)
if ok {
rdb := redisCache.GetRedisClient().GetClient()
pipe := rdb.Pipeline()
pipe.HSet(ctx, hashKey, fmt.Sprintf("%d", msg.Seq), string(data))
pipe.ZAdd(ctx, indexKey, redis.Z{Score: float64(msg.Seq), Member: fmt.Sprintf("%d", msg.Seq)})
pipe.Expire(ctx, hashKey, c.settings.MessageDetailTTL)
pipe.Expire(ctx, indexKey, c.settings.MessageIndexTTL)
pipe.Incr(ctx, countKey)
if _, err := pipe.Exec(ctx); err != nil {
return fmt.Errorf("failed to pipeline cache message: %w", err)
}
return nil
}
if err := c.cache.HSet(ctx, MessageHashKey(convID), fmt.Sprintf("%d", msg.Seq), string(data)); err != nil {
return fmt.Errorf("failed to set hash: %w", err)
}
// ZADD 消息索引
if err := c.cache.ZAdd(ctx, indexKey, float64(msg.Seq), fmt.Sprintf("%d", msg.Seq)); err != nil {
if err := c.cache.ZAdd(ctx, MessageIndexKey(convID), float64(msg.Seq), fmt.Sprintf("%d", msg.Seq)); err != nil {
return fmt.Errorf("failed to add to sorted set: %w", err)
}
// 设置 TTL
c.cache.Expire(ctx, hashKey, c.settings.MessageDetailTTL)
c.cache.Expire(ctx, indexKey, c.settings.MessageIndexTTL)
// INCR 消息计数
c.cache.Expire(ctx, MessageHashKey(convID), c.settings.MessageDetailTTL)
c.cache.Expire(ctx, MessageIndexKey(convID), c.settings.MessageIndexTTL)
c.cache.Incr(ctx, MessageCountKey(convID))
return nil
}