输入关键词开始搜索

模块详细设计

05 — 云融(YunRong)· 模块详细设计文档

前置文档02-系统架构设计文档03-通信协议详细设计文档04-数据库设计文档 设计思路00-设计思路文档

版本:v0.1 状态:已发布 最后更新:2025-01


1. 文档概述

本文档对系统架构中定义的各模块进行详细设计,包含:

  • 类接口定义(头文件级别的 API 契约)
  • 核心算法伪代码
  • 关键时序图
  • 线程交互细节

所有代码示例使用 C++17,遵循项目编码规范。


2. 网络通信模块 (core/net/)

⭐ 核心强化模块。这是整个项目中设计密度最高的部分。

2.1 模块内部类图

┌─────────────────────────────────────────────────────────┐
│                  ConnectionManager (Facade)              │
│  + connect(serverUrl)                                   │
│  + disconnect()                                         │
│  + sendMessage(msg, callback)                           │
│  + sendRequest(cmd)                                     │
│  + getConnectionState() -> ConnectionState              │
│  signal: onStateChanged(ConnectionState)                │
│  signal: onMessageReceived(Message)                     │
└───────────┬─────────────────────────────┬───────────────┘
            │                             │
    ┌───────▼────────┐          ┌─────────▼──────────┐
    │   WsClient      │          │   HttpClient        │
    │ + connect(url)  │          │ + get(url, cb)      │
    │ + sendText(json)│          │ + post(url,body,cb) │
    │ + sendBinary(b) │          │ + upload(file, cb)  │
    │ signal: onText  │          │ + download(url,cb)  │
    │ signal: onBinary│          │ signal: onProgress  │
    └───────┬─────────┘          └─────────────────────┘

    ┌───────▼──────────┐
    │    Protocol       │  (无状态,纯函数)
    │ + encode(msg)->str│
    │ + decode(str)->msg│
    │ + encodeBinary()  │
    │ + decodeBinary()  │
    │ + validate(json)  │
    └──────────────────┘

┌──────────────┐   ┌──────────────────┐   ┌──────────────┐
│  Heartbeat   │   │ ReconnectStrategy│   │ RateLimiter  │
│ + start()    │   │ + nextDelay(n)   │   │ + tryConsume │
│ + stop()     │   │ + reset()        │   │ + setLimit   │
│ signal:overtime│ │ + shouldRetry()  │   └──────────────┘
└──────────────┘   └──────────────────┘

2.2 核心接口定义

2.2.1 IConnection — 连接抽象

// core/net/i_connection.h
#pragma once

#include <functional>
#include <string>
#include <vector>
#include <cstdint>

namespace core::net {

enum class ConnectionState {
    Idle,
    Connecting,
    Connected,
    Reconnecting,
    Error
};

// 所有连接类型的基类接口
class IConnection {
public:
    using TextCallback   = std::function<void(const std::string& json)>;
    using BinaryCallback = std::function<void(const std::vector<uint8_t>& data)>;
    using StateCallback  = std::function<void(ConnectionState)>;
    using ErrorCallback  = std::function<void(int code, const std::string& msg)>;

    virtual ~IConnection() = default;

    virtual void connect(const std::string& url) = 0;
    virtual void disconnect() = 0;
    virtual void sendText(const std::string& json) = 0;
    virtual void sendBinary(const std::vector<uint8_t>& data) = 0;
    virtual ConnectionState state() const = 0;

    virtual void setOnText(TextCallback cb) = 0;
    virtual void setOnBinary(BinaryCallback cb) = 0;
    virtual void setOnStateChanged(StateCallback cb) = 0;
    virtual void setOnError(ErrorCallback cb) = 0;
};

} // namespace core::net

2.2.2 ConnectionManager — 连接管理器(外观)

// core/net/connection_mgr.h
#pragma once

#include "i_connection.h"
#include "protocol.h"
#include "heartbeat.h"
#include "reconnect_strategy.h"
#include "rate_limiter.h"
#include "../model/message.h"
#include "../infra/thread_pool.h"
#include <memory>
#include <queue>
#include <unordered_map>

namespace core::net {

// 网络请求命令(Command 模式)
struct NetCommand {
    enum class Type { SendMessage, MarkRead, SetStatus, FileCheck };
    Type type;
    std::string payload_json;
    uint32_t seq = 0;
    int retry_count = 0;
    std::function<void(bool, const std::string&)> callback;
};

class ConnectionManager : public QObject {
    Q_OBJECT

public:
    explicit ConnectionManager(infra::ThreadPool& pool);
    ~ConnectionManager();

    // ---- 生命周期 ----
    void connectToServer(const std::string& url, const std::string& token);
    void disconnect();

    // ---- 消息发送 (Command 模式) ----
    void sendMessage(const model::Message& msg,
                     std::function<void(bool, const std::string&)> callback);
    void markRead(uint64_t convId, uint32_t readToSeq);
    void sendHeartbeat();

    // ---- 文件操作 ----
    void uploadFile(const std::string& path,
                    std::function<void(double progress)> progressCb,
                    std::function<void(bool, std::string url)> doneCb);
    void downloadFile(const std::string& url, const std::string& savePath,
                      std::function<void(double progress)> progressCb,
                      std::function<void(bool)> doneCb);

    // ---- 状态 ----
    ConnectionState connectionState() const { return state_; }
    double bytesPerSecondUp()   const { return rateLimiter_.upSpeed(); }
    double bytesPerSecondDown() const { return rateLimiter_.downSpeed(); }

signals:
    void onStateChanged(ConnectionState newState);
    void onMessageReceived(model::Message msg);
    void onNotificationReceived(model::Task notification);
    void onUploadProgress(const std::string& transferId, double progress);
    void onDownloadProgress(const std::string& transferId, double progress);

private:
    // 内部组件
    std::unique_ptr<IConnection> wsClient_;
    std::unique_ptr<IConnection> httpClient_;
    Protocol protocol_;
    Heartbeat heartbeat_;
    ReconnectStrategy reconnect_;
    RateLimiter rateLimiter_;
    infra::ThreadPool& threadPool_;

    // 状态
    ConnectionState state_ = ConnectionState::Idle;
    std::string serverUrl_;
    std::string authToken_;

    // 待确认帧表
    std::unordered_map<uint32_t, NetCommand> pendingCommands_;
    uint32_t nextSeq_ = 1;

    // 连接状态机驱动
    void onWsConnected();
    void onWsDisconnected();
    void onHeartbeatTimeout();
    void startReconnect();
    void onReconnected();

    // 协议处理
    void handleIncomingFrame(const std::string& json);
    void handleAck(uint32_t ackedSeq);
    void handleRetransmit();
    void retryCommand(uint32_t seq);
};

} // namespace core::net

2.2.3 Protocol — 协议编解码器

// core/net/protocol.h
#pragma once

#include "../model/message.h"
#include "../model/task.h"
#include <nlohmann/json.hpp>
#include <string>
#include <vector>
#include <optional>
#include <cstdint>

namespace core::net {

using json = nlohmann::json;

// 帧类型枚举
enum class FrameType : uint8_t {
    // JSON 帧
    Auth        = 0,
    Msg         = 1,
    Ack         = 2,
    Ping        = 3,
    Pong        = 4,
    Sync        = 5,
    SyncData    = 6,
    Notify      = 7,
    MsgRead     = 8,
    Error       = 9,
    // 二进制帧
    Thumbnail   = 0x01,
    FileChunk   = 0x02,
    FileChunkAck= 0x03,
    FileMeta    = 0x04,
};

class Protocol {
public:
    // ---- JSON 帧编码 ----
    std::string encodeAuth(const std::string& token, uint32_t seq);
    std::string encodeMessage(const model::Message& msg, uint32_t seq);
    std::string encodeAck(uint32_t ackSeq);
    std::string encodePing();
    std::string encodeSync(uint32_t lastSeq, uint64_t lastTs, uint16_t limit);

    // ---- JSON 帧解码 ----
    enum class DecodedType { Message, Notification, Ack, AuthOk, AuthFail,
                             SyncData, StateChange, Error, Unknown };
    struct DecodedFrame {
        DecodedType type = DecodedType::Unknown;
        uint32_t seq = 0;
        uint64_t ts = 0;
        json payload;
    };
    DecodedFrame decode(const std::string& json);

    // ---- 二进制帧 ----
    std::vector<uint8_t> encodeFileChunk(uint16_t index, uint32_t offset,
                                          const uint8_t* data, size_t len);
    std::vector<uint8_t> encodeFileMeta(const std::string& name, uint64_t size,
                                         const std::string& hash, uint32_t chunkSize);
    struct FileChunkAck {
        uint16_t receivedCount;
        std::vector<bool> bitmap;
    };
    FileChunkAck decodeFileChunkAck(const std::vector<uint8_t>& data);

    // ---- 校验 ----
    std::optional<std::string> validate(const json& frame);
    static uint32_t extractSeq(const json& frame);
};

} // namespace core::net

2.2.4 ReconnectStrategy — 重连策略

// core/net/reconnect_strategy.h
#pragma once

#include <chrono>
#include <cstdint>

namespace core::net {

class ReconnectStrategy {
public:
    static constexpr int MAX_RETRIES = 10;

    ReconnectStrategy();

    // 获取下一次重试的延迟
    std::chrono::milliseconds nextDelay();

    // 是否还应该继续重试
    bool shouldRetry() const { return attempt_ < MAX_RETRIES; }

    // 重置计数器(连接成功后调用)
    void reset();

    int attempt() const { return attempt_; }

private:
    int attempt_ = 0;
};

// 实现
inline std::chrono::milliseconds ReconnectStrategy::nextDelay() {
    attempt_++;
    if (attempt_ <= 4) {
        return std::chrono::milliseconds(1000 * (1 << (attempt_ - 1)));
        // 1s, 2s, 4s, 8s
    } else if (attempt_ <= 6) {
        return std::chrono::milliseconds(attempt_ == 5 ? 30000 : 60000);
        // 30s, 60s
    } else {
        return std::chrono::milliseconds(60000);
        // 封顶 60s
    }
}

} // namespace core::net

3. 数据访问模块 (core/data/)

3.1 接口与实现类图

┌──────────────────────────────────────┐
│          IDataStore (接口)            │
│  + storeMessage(msg) -> bool         │
│  + getMessages(convId, from, n, cb)  │
│  + searchMessages(keyword, cb)       │
│  + upsertContact(contact)            │
│  + getConversations(cb)              │
│  + updateUnreadCount(convId, delta)  │
│  + saveDraft(convId, text)           │
│  + getConfig(key) -> optional<string>│
│  + setConfig(key, value)             │
│  + pushOfflineOp(op)                 │
│  + getOfflineOps() -> vector<Op>     │
└──────┬───────────────┬───────────────┘
       │               │
┌──────▼──────┐  ┌─────▼─────────┐
│ SqliteStore │  │  PgsqlStore   │
│ (本地存储)   │  │  (远程存储)    │
└─────────────┘  └───────────────┘

┌──────────────────────────────────────┐
│           SyncManager                │
│  + startSync()                       │
│  + replayOfflineQueue()              │
│  + getSyncState() -> SyncState       │
│  signal: onSyncComplete()            │
│  signal: onSyncProgress(double)      │
└──────────────────────────────────────┘

┌──────────────────────────────────────┐
│           CacheManager               │
│  + getContact(id) -> optional<Contact>│
│  + putContact(id, contact)           │
│  + warmCache(contacts)               │
│  + getOnlineContacts() -> set<id>    │
│  (内部使用 unordered_map + shared_mutex)│
└──────────────────────────────────────┘

3.2 IDataStore 完整接口

// core/data/i_data_store.h
#pragma once

#include "../model/message.h"
#include "../model/conversation.h"
#include "../model/contact.h"
#include "../model/task.h"
#include "../model/file_transfer.h"
#include <string>
#include <vector>
#include <optional>
#include <functional>

namespace core::data {

using MessageCallback   = std::function<void(std::vector<model::Message>)>;
using ConvCallback      = std::function<void(std::vector<model::Conversation>)>;
using ContactCallback   = std::function<void(std::vector<model::Contact>)>;
using TaskCallback      = std::function<void(std::vector<model::Task>)>;

struct OfflineOp {
    int64_t id;
    std::string operation;
    std::string payloadJson;
    int64_t createdAt;
};

// Strategy 模式的核心接口
class IDataStore {
public:
    virtual ~IDataStore() = default;

    // ---- 消息 ----
    virtual bool storeMessage(const model::Message& msg) = 0;
    virtual void getMessages(int64_t convId, uint64_t beforeTs,
                             int limit, MessageCallback cb) = 0;
    virtual void searchMessages(const std::string& keyword,
                                MessageCallback cb) = 0;
    virtual void markMessageSynced(const std::string& msgId) = 0;
    virtual void markMessageFailed(const std::string& msgId) = 0;

    // ---- 会话 ----
    virtual void getConversations(ConvCallback cb) = 0;
    virtual void updateLastMessage(int64_t convId, const std::string& preview,
                                    uint64_t ts) = 0;
    virtual void incrementUnread(int64_t convId, int delta) = 0;
    virtual void clearUnread(int64_t convId) = 0;

    // ---- 联系人 ----
    virtual void upsertContact(const model::Contact& c) = 0;
    virtual void getContacts(ContactCallback cb) = 0;
    virtual void searchContacts(const std::string& query,
                                ContactCallback cb) = 0;

    // ---- 任务/通知 ----
    virtual void storeTask(const model::Task& t) = 0;
    virtual void getTasks(TaskCallback cb) = 0;
    virtual void updateTaskStatus(int64_t id, int status) = 0;

    // ---- 文件传输 ----
    virtual void saveTransferState(const model::FileTransfer& ft) = 0;
    virtual void updateTransferProgress(const std::string& transferId,
                                         int completedChunks) = 0;
    virtual void getActiveTransfers(
        std::function<void(std::vector<model::FileTransfer>)> cb) = 0;

    // ---- 草稿 ----
    virtual void saveDraft(int64_t convId, const std::string& text) = 0;
    virtual std::optional<std::string> getDraft(int64_t convId) = 0;
    virtual void deleteDraft(int64_t convId) = 0;

    // ---- 配置 ----
    virtual std::optional<std::string> getConfig(const std::string& key) = 0;
    virtual void setConfig(const std::string& key, const std::string& val) = 0;

    // ---- 离线队列 ----
    virtual void pushOfflineOp(const OfflineOp& op) = 0;
    virtual std::vector<OfflineOp> getPendingOfflineOps() = 0;
    virtual void markOfflineOpDone(int64_t id) = 0;
    virtual void markOfflineOpFailed(int64_t id) = 0;

    // ---- 维护 ----
    virtual void cleanup() = 0;       // 清理过期数据
    virtual int64_t dbSize() = 0;     // 数据库文件大小
};

} // namespace core::data

3.3 SqliteStore 关键实现骨架

// core/data/sqlite_store.h
#pragma once

#include "i_data_store.h"
#include <sqlite3.h>
#include <memory>
#include <string>

namespace core::data {

class SqliteStore : public IDataStore {
public:
    explicit SqliteStore(const std::string& dbPath, const std::string& key = "");
    ~SqliteStore() override;

    // IDataStore 全部方法实现 ...

private:
    sqlite3* db_ = nullptr;
    std::string dbPath_;

    // 预编译语句缓存
    struct PreparedStatements {
        sqlite3_stmt* insert_msg       = nullptr;
        sqlite3_stmt* query_msg_by_conv = nullptr;
        sqlite3_stmt* search_msg_fts   = nullptr;
        sqlite3_stmt* upsert_contact   = nullptr;
        sqlite3_stmt* upsert_conv      = nullptr;
        sqlite3_stmt* inc_unread       = nullptr;
        // ... 所有常用语句
    } stmt_;

    void prepareAll();                         // 启动时预编译
    void finalizeAll();                        // 关闭时释放
    void applyPragmas();                        // WAL / cache_size 等
    void migrateIfNeeded();                     // schema 迁移

    // 线程安全:此对象只在 DB Thread 中被调用,无需加锁
};

} // namespace core::data

3.4 SyncManager 算法

// core/data/sync_mgr.h
#pragma once

#include "i_data_store.h"
#include <memory>

namespace core::data {

enum class SyncState { Idle, InProgress, Complete, Failed };

class SyncManager {
public:
    SyncManager(std::shared_ptr<IDataStore> local,
                std::shared_ptr<IDataStore> remote,
                std::function<void(const std::string& json)> wsSender);

    void startSync();
    void onSyncDataReceived(const std::string& json);
    void replayOfflineQueue();

    SyncState state() const { return state_; }

private:
    std::shared_ptr<IDataStore> local_;
    std::shared_ptr<IDataStore> remote_;
    std::function<void(const std::string&)> wsSender_;
    SyncState state_ = SyncState::Idle;
    bool hasMore_ = false;
    uint32_t lastServerSeq_ = 0;
};

// ---- 核心算法 ----

inline void SyncManager::startSync() {
    state_ = SyncState::InProgress;

    // 1. 读取本地水位线
    auto lastSeqStr = local_->getConfig("sync.last_seq");
    lastServerSeq_ = lastSeqStr ? std::stoul(*lastSeqStr) : 0;
    auto lastTsStr  = local_->getConfig("sync.last_ts");
    uint64_t lastTs = lastTsStr ? std::stoull(*lastTsStr) : 0;

    // 2. 发送 sync 请求(通过 WebSocket)
    auto syncFrame = Protocol::encodeSync(lastServerSeq_, lastTs, 200);
    wsSender_(syncFrame);
}

inline void SyncManager::onSyncDataReceived(const std::string& json) {
    auto frame = Protocol::decode(json);

    // 3. 批量写入本地
    for (auto& msg : frame.payload["messages"]) {
        local_->storeMessage(parseMessage(msg));
    }
    for (auto& contact : frame.payload.value("contacts_delta", json::array())) {
        local_->upsertContact(parseContact(contact));
    }

    // 4. 更新水位线
    uint32_t endSeq = frame.payload["end_seq"];
    local_->setConfig("sync.last_seq", std::to_string(endSeq));

    // 5. 检查是否还有更多
    hasMore_ = frame.payload.value("has_more", false);
    if (hasMore_) {
        lastServerSeq_ = endSeq;
        startSync(); // 继续拉取下一批
    } else {
        // 6. 同步完成 → 重放离线队列
        state_ = SyncState::Complete;
        replayOfflineQueue();
    }
}

} // namespace core::data

4. 业务服务模块 (core/service/)

4.1 IMService

// core/service/im_service.h
#pragma once

#include "../net/connection_mgr.h"
#include "../data/i_data_store.h"
#include "../model/message.h"
#include "../model/conversation.h"
#include <memory>

namespace core::service {

class IMService : public QObject {
    Q_OBJECT

public:
    IMService(std::shared_ptr<net::ConnectionManager> net,
              std::shared_ptr<data::IDataStore> store);

    // 发送消息
    void sendTextMessage(int64_t convId, const std::string& text,
                         const std::string& quoteMsgId = "");

    // 获取会话列表(优先本地)
    void loadConversations(std::function<void(std::vector<model::Conversation>)> cb);

    // 获取消息历史(本地分页)
    void loadMessages(int64_t convId, uint64_t beforeTs, int limit,
                      std::function<void(std::vector<model::Message>)> cb);

    // 搜索消息
    void searchMessages(const std::string& keyword,
                        std::function<void(std::vector<model::Message>)> cb);

    // 标记已读
    void markConversationRead(int64_t convId);

    // 处理收到的消息
    void onIncomingMessage(const model::Message& msg);

signals:
    void conversationUpdated(const model::Conversation& conv);
    void newMessageReceived(const model::Message& msg);
    void unreadCountChanged(int totalUnread);

private:
    std::shared_ptr<net::ConnectionManager> net_;
    std::shared_ptr<data::IDataStore> store_;

    // 消息处理管线(Decorator 模式)
    void processIncomingMessage(const model::Message& msg);
    bool validateMessage(const model::Message& msg);      // 校验
    bool deduplicateMessage(const model::Message& msg);   // 去重
    void persistMessage(const model::Message& msg);       // 存储
    void notifyUI(const model::Message& msg);             // 通知 GUI
};

} // namespace core::service

4.2 FileService — 文件传输编排

// core/service/file_service.h
#pragma once

#include "../net/connection_mgr.h"
#include "../data/i_data_store.h"
#include "../model/file_transfer.h"
#include <memory>
#include <functional>

namespace core::service {

class FileService : public QObject {
    Q_OBJECT

public:
    FileService(std::shared_ptr<net::ConnectionManager> net,
                std::shared_ptr<data::IDataStore> store);

    // 上传文件
    void uploadFile(const std::string& localPath, int64_t convId = 0);

    // 下载文件
    void downloadFile(const std::string& remoteUrl, const std::string& savePath);

    // 获取传输历史
    void getTransferHistory(
        std::function<void(std::vector<model::FileTransfer>)> cb);

    // 取消传输
    void cancelTransfer(const std::string& transferId);
    void pauseTransfer(const std::string& transferId);
    void resumeTransfer(const std::string& transferId);

signals:
    void transferProgressChanged(const std::string& transferId,
                                  double progress, double speedBps);
    void transferCompleted(const std::string& transferId);
    void transferFailed(const std::string& transferId, const std::string& error);

private:
    std::shared_ptr<net::ConnectionManager> net_;
    std::shared_ptr<data::IDataStore> store_;

    // 活跃传输任务(内存中)
    std::unordered_map<std::string, model::FileTransfer> activeTransfers_;

    // 上传编排
    void doUpload(const model::FileTransfer& task);
    void uploadNextChunk(const model::FileTransfer& task);
    void onChunkSent(const std::string& transferId, int chunkIndex, bool ok);

    // 下载编排
    void doDownload(const model::FileTransfer& task);
    void downloadNextChunk(const model::FileTransfer& task);
    void onChunkReceived(const std::string& transferId, int chunkIndex,
                          const std::vector<uint8_t>& data);
};

} // namespace core::service

5. 基础设施模块 (core/infra/)

5.1 ThreadPool

// core/infra/thread_pool.h
#pragma once

#include <thread>
#include <vector>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <future>

namespace core::infra {

class ThreadPool {
public:
    explicit ThreadPool(size_t numThreads = std::thread::hardware_concurrency());
    ~ThreadPool();

    // 提交任务,返回 future
    template<typename F, typename... Args>
    auto submit(F&& f, Args&&... args)
        -> std::future<typename std::invoke_result<F, Args...>::type>;

    size_t workerCount() const { return workers_.size(); }
    size_t pendingTasks() const;

    void resize(size_t newSize);

private:
    std::vector<std::thread> workers_;
    std::queue<std::function<void()>> tasks_;
    std::mutex queueMutex_;
    std::condition_variable cv_;
    bool stop_ = false;
};

} // namespace core::infra

5.2 MessageBus — 线程安全通信

// core/infra/msg_bus.h
#pragma once

#include <queue>
#include <mutex>
#include <functional>
#include <memory>

namespace core::infra {

// 线程安全的事件总线,基于 std::queue + std::mutex
// 注:若性能成为瓶颈可替换为定制环形缓冲区
template<typename T>
class MessageBus {
public:
    explicit MessageBus(size_t capacity = 4096)
        : capacity_(capacity) {}

    // 生产者端(任意线程调用)
    bool publish(T event) {
        std::lock_guard<std::mutex> lock(mutex_);
        if (queue_.size() >= capacity_) return false;
        queue_.push(std::move(event));
        return true;
    }

    // 消费者端(通常在 GUI 线程批量消费)
    template<typename Consumer>
    void consume(Consumer&& consumer) {
        std::lock_guard<std::mutex> lock(mutex_);
        while (!queue_.empty()) {
            consumer(std::move(queue_.front()));
            queue_.pop();
        }
    }

    // 非阻塞消费一条
    bool tryConsume(T& event) {
        std::lock_guard<std::mutex> lock(mutex_);
        if (queue_.empty()) return false;
        event = std::move(queue_.front());
        queue_.pop();
        return true;
    }

    size_t capacity() const { return capacity_; }
    bool empty() const {
        std::lock_guard<std::mutex> lock(mutex_);
        return queue_.empty();
    }

private:
    std::queue<T> queue_;
    mutable std::mutex mutex_;
    size_t capacity_;
};

// 跨线程信号投递:将事件从工作线程安全投递到 Qt 主线程
class QtSignalBridge : public QObject {
    Q_OBJECT
public:
    template<typename Event>
    void postToMainThread(Event&& event,
                          std::function<void(Event)> handler) {
        // 使用 QMetaObject::invokeMethod 将 handler 的执行调度到主线程事件循环
        QMetaObject::invokeMethod(this, [handler, e = std::forward<Event>(event)]() {
            handler(e);
        }, Qt::QueuedConnection);
    }
};

} // namespace core::infra

5.3 ConfigManager

// core/infra/config_mgr.h
#pragma once

#include <string>
#include <optional>
#include <shared_mutex>
#include <unordered_map>

namespace core::infra {

// Meyer's Singleton — 线程安全的单例
class ConfigManager {
public:
    static ConfigManager& instance() {
        static ConfigManager mgr;
        return mgr;
    }

    // 禁止拷贝和移动
    ConfigManager(const ConfigManager&) = delete;
    ConfigManager& operator=(const ConfigManager&) = delete;

    // 类型安全的配置访问
    template<typename T>
    T get(const std::string& key, T defaultValue = T{}) const;

    template<typename T>
    void set(const std::string& key, const T& value);

    std::optional<std::string> getRaw(const std::string& key) const;
    void setRaw(const std::string& key, const std::string& value);

    void loadFromFile(const std::string& path);
    void saveToFile(const std::string& path);

private:
    ConfigManager() = default;

    mutable std::shared_mutex mutex_;    // 多读单写
    std::unordered_map<std::string, std::string> config_;
};

} // namespace core::infra

6. GUI 模块 (gui/)

6.1 MainWindow 结构

// gui/main_window.h
#pragma once

#include <QMainWindow>
#include <QStackedWidget>
#include <QSystemTrayIcon>
#include <memory>

namespace gui {

class ChatView;
class ContactTree;
class TaskList;
class FilePanel;
class Dashboard;

class MainWindow : public QMainWindow {
    Q_OBJECT

public:
    explicit MainWindow(QWidget* parent = nullptr);
    ~MainWindow();

private:
    void setupUI();
    void setupTrayIcon();
    void connectSignals();

    // 导航
    enum class Page { Chat, Tasks, Files, Contacts, Settings };
    void switchPage(Page page);

    // 三段式布局
    QWidget*     navBar_;         // 左侧导航栏
    QStackedWidget* contentArea_; // 中间内容区
    QWidget*     infoPanel_;      // 右侧信息面板(可选)

    // 子视图
    ChatView*    chatView_ = nullptr;
    TaskList*    taskList_ = nullptr;
    FilePanel*   filePanel_ = nullptr;
    ContactTree* contactTree_ = nullptr;
    Dashboard*   dashboard_ = nullptr;

    // 系统托盘
    QSystemTrayIcon* trayIcon_ = nullptr;
    int totalUnread_ = 0;
};

} // namespace gui

6.2 ChatView — 消息列表性能设计

// gui/views/chat_view.h
#pragma once

#include <QWidget>
#include <QListView>
#include <QTextEdit>
#include <QPushButton>
#include <memory>

namespace gui {

// 消息列表 Model(适配 core::model::Message → Qt Model)
class MessageListModel : public QAbstractListModel {
    Q_OBJECT
public:
    enum Roles {
        ContentTypeRole = Qt::UserRole + 1,
        SenderNameRole,
        TimestampRole,
        StatusRole,
        IsMineRole,
        ContentBodyRole,
    };

    int rowCount(const QModelIndex& parent = QModelIndex{}) const override;
    QVariant data(const QModelIndex& index, int role) const override;

    // 高效追加(避免 reset 整个 Model)
    void appendMessages(const std::vector<model::Message>& msgs);
    void prependMessages(const std::vector<model::Message>& msgs); // 向上翻页加载
    void updateMessageStatus(const std::string& msgId, int status);

private:
    std::vector<model::Message> messages_;
    // 上限:最多保留 10000 条在 Model 中
    static constexpr size_t MAX_CACHED = 10000;
};

class ChatView : public QWidget {
    Q_OBJECT
public:
    explicit ChatView(QWidget* parent = nullptr);

    void loadConversation(int64_t convId);
    void appendMessage(const model::Message& msg);
    void updateMessageStatus(const std::string& msgId, int status);

private:
    QListView*    messageList_ = nullptr;
    QTextEdit*    inputBox_    = nullptr;
    QPushButton*  sendBtn_     = nullptr;

    MessageListModel* model_   = nullptr;
    int64_t currentConvId_     = 0;

    // 滚动位置检测:如果用户在查看历史消息,新消息不自动滚到底部
    bool isAtBottom_ = true;

    void onSendClicked();
    void onScrollChanged(int value);
    void loadMoreHistory(); // 向上滚动到顶 → 加载更早的消息
};

} // namespace gui

6.3 自定义控件 — MessageBubble Delegate

// gui/delegates/message_delegate.h
#pragma once

#include <QStyledItemDelegate>
#include <QPainter>

namespace gui {

class MessageDelegate : public QStyledItemDelegate {
    Q_OBJECT
public:
    explicit MessageDelegate(QObject* parent = nullptr);

    void paint(QPainter* painter, const QStyleOptionViewItem& option,
               const QModelIndex& index) const override;

    QSize sizeHint(const QStyleOptionViewItem& option,
                   const QModelIndex& index) const override;

private:
    // 气泡绘制
    void paintBubble(QPainter* p, const QRect& rect,
                     const QString& text, bool isMine) const;
    void paintImageContent(QPainter* p, const QRect& rect,
                           const QString& url, const QSize& imgSize) const;
    void paintFileCard(QPainter* p, const QRect& rect,
                       const QString& fileName, int64_t fileSize) const;

    // 布局计算
    QRect bubbleRect(const QStyleOptionViewItem& option, bool isMine) const;
    QSize textSize(const QString& text, int maxWidth) const;

    // 样式常量
    static constexpr int BUBBLE_RADIUS = 12;
    static constexpr int BUBBLE_PADDING = 10;
    static constexpr int MAX_BUBBLE_WIDTH = 400;
    QColor myBubbleColor_    = QColor("#95EC69");   // 微信绿
    QColor otherBubbleColor_ = QColor("#FFFFFF");
};

} // namespace gui

7. 关键时序图

7.1 消息发送全链路

User           Main Thread        Worker Pool          Server
 │                 │                   │                   │
 │ 点击发送         │                   │                   │
 │────────────────►│                   │                   │
 │                 │                   │                   │
 │                 │ 1. IMService::    │                   │
 │                 │    sendTextMessage│                   │
 │                 │                   │                   │
 │                 │ 2. 追加到聊天界面   │                   │
 │◄─ 气泡:"发送中"  │                   │                   │
 │                 │                   │                   │
 │                 │ 3. 投递编码任务     │                   │
 │                 │──────────────────►│                   │
 │                 │                   │ 4. Protocol 编码   │
 │                 │                   │    分配 seq=42    │
 │                 │                   │    序列化 JSON     │
 │                 │                   │    入 pending 表   │
 │                 │◄── EncodedFrame ──│                   │
 │                 │                   │                   │
 │                 │ 5. WS send ──────────────────────────►│
 │                 │                   │                   │
 │                 │◄── Ack(seq=42) ──────────────────────│
 │                 │                   │                   │
 │                 │ 6. 从 pending 移除 │                   │
 │                 │ 7. 更新 SQLite     │                   │
 │                 │    (synced=1,      │                   │
 │                 │     status=已送达)  │                   │
 │                 │                   │                   │
 │◄─ 气泡:"已送达"  │                   │                   │

7.2 文件上传全链路

User         Main Thread      FileService       File Pool         Server
 │                │                │                │                │
 │ 拖入文件       │                │                │                │
 │───────────────►│                │                │                │
 │                │ uploadFile()   │                │                │
 │                │───────────────►│                │                │
 │                │                │ 1. SHA-256     │                │
 │                │                │    文件哈希     │                │
 │                │                │                │                │
 │                │                │ 2. 秒传检查     │                │
 │                │                │──────────────────────────────►│
 │                │                │◄─── {exists:false} ──────────│
 │                │                │                │                │
 │                │                │ 3. 建传输任务    │                │
 │                │                │   存 SQLite     │                │
 │                │                │                │                │
 │◄── 进度: 0%    │                │                │                │
 │                │                │                │                │
 │                │                │ 4. 分块循环     │                │
 │                │                │───────────────►│                │
 │                │                │                │─ Chunk 0 ────►│
 │                │                │                │─ Chunk 1 ────►│
 │                │◄── progress ───│◄───────────────│                │
 │◄── 进度: 10%   │                │                │                │
 │                │                │      ...       │     ...       │
 │                │                │                │─ Chunk N ────►│
 │                │                │                │                │
 │                │                │                │◄── complete ──│
 │                │                │                │                │
 │                │                │ 5. 更新 SQLite  │                │
 │                │                │    status=完成   │                │
 │                │                │                │                │
 │                │                │ 6. 发送文件消息   │                │
 │                │                │──────────────────────────────►│
 │                │                │                │                │
 │◄── 上传完成    │                │                │                │

7.3 网络断开→重连→恢复

Main Thread            Worker Pool           DB Thread
     │                      │                     │
     │ [心跳连续3次超时]      │                     │
     │ 状态→RECONNECTING     │                     │
     │ 显示:"连接断开,重连中"  │                     │
     │                      │                     │
     │ 退避 1s/2s/4s 后重试   │                     │
     │── TCP+TLS+WS ──── ✓   │                     │
     │                      │                     │
     │ 发送 auth 帧           │                     │
     │◄── auth_ok            │                     │
     │                      │                     │
     │ 检查 seq → 有缺口      │                     │
     │ 发送 sync(last_seq)   │                     │
     │                      │                     │
     │◄── sync_data (200条)  │                     │
     │                      │                     │
     │── RawBytes ─────────►│                     │
     │                      │ 1. 批量解码/校验     │
     │◄── DecodedMsgs ──────│                     │
     │                      │                     │
     │── StoreBatch ───────────────────►[Signal]──│
     │                      │       2. 批量写入     │
     │                      │                     │
     │◄── BatchDone ──────────────────[Signal]───│
     │ 消息列表刷新           │                     │
     │                      │                     │
     │◄── sync_data(has_more=f)                   │
     │                      │                     │
     │── 拉取离线队列 ─────────────────►[Signal]──│
     │                      │    查询 offline_queue│
     │◄── [send_msg, ...] ───────────[Signal]────│
     │                      │                     │
     │── 重放队列 ──────────►│                     │
     │                      │ 3. 逐个编码/发送     │
     │                      │    服务端去重+ACK    │
     │                      │                     │
     │ 状态→CONNECTED        │                     │
     │ 显示:"已连接"          │                     │

8. 错误处理策略

8.1 分层错误处理

网络层错误
  ├── 连接失败 → 自动重连 (ReconnectStrategy)
  ├── 超时 → 重试 or 失败回调
  ├── TLS 证书错误 → 通知用户(开发模式可忽略)
  └── 帧解析失败 → 发送 error 帧 + 记录日志

数据层错误
  ├── SQLite BUSY → 重试 3 次 (WAL 模式下极少发生)
  ├── 磁盘满 → 通知用户清理
  ├── 迁移失败 → 备份旧 DB + 重建
  └── 加密密钥错误 → 提示重新登录

业务层错误
  ├── 会话不存在 → 静默忽略 + 清理本地数据
  ├── 权限不足 → Toast 提示
  └── Token 过期 → 自动刷新 or 跳转登录

GUI 层错误
  ├── 网络层未就绪 → 按钮灰化
  └── 加载超时 → 骨架屏 + 重试按钮

8.2 错误传播模式

// 使用 std::optional 或 Result 类型
template<typename T>
struct Result {
    bool ok;
    T value;
    std::string error;
    int code = 0;
};

// 示例:带错误信息的回调
using SendResultCallback = std::function<void(Result<std::string> msgId)>;

// 错误日志
#define LOG_NET_ERROR(code, msg) \
    Logger::error("NET", "{}:{} code={} msg={}", __FILE__, __LINE__, code, msg)

9. 数据模型定义 (core/model/)

9.1 核心数据结构

// core/model/message.h
#pragma once

#include <string>
#include <cstdint>

namespace core::model {

struct Message {
    std::string msgId;           // 全局唯一 ID
    int64_t     convId = 0;      // 会话 ID
    int64_t     senderId = 0;
    std::string senderName;
    int         contentType = 0; // 0=文本, 1=图片, 2=文件, 3=系统
    std::string contentBody;     // JSON
    int         status = 0;      // 0=发送中, 1=已送达, 2=已读, 3=失败
    uint32_t    seq = 0;
    uint64_t    timestamp = 0;
    bool        isMine = false;
};

struct Conversation {
    int64_t     id = 0;
    int         type = 0;        // 0=私聊, 1=群聊
    std::string title;
    std::string avatarUrl;
    std::string lastMsgPreview;
    uint64_t    lastMsgTime = 0;
    uint32_t    lastMsgSeq = 0;
    int         unreadCount = 0;
    bool        isPinned = false;
    bool        isMuted = false;
};

struct Contact {
    int64_t     id = 0;
    std::string name;
    std::string avatarUrl;
    std::string department;
    std::string title;
    int         status = 0;      // 0=离线, 1=在线
    bool        isFavorite = false;
};

struct Task {
    int64_t     id = 0;
    std::string notifyId;
    std::string taskType;
    std::string title;
    std::string body;
    int         priority = 1;
    int         status = 0;      // 0=未读, 1=已读, 2=已处理
    int64_t     fromUserId = 0;
    std::string fromUserName;
    uint64_t    createdAt = 0;
};

struct FileTransfer {
    std::string transferId;
    std::string fileName;
    std::string filePath;
    int64_t     fileSize = 0;
    std::string fileHash;
    int         direction = 0;   // 0=上传, 1=下载
    int64_t     convId = 0;
    int         totalChunks = 0;
    int         completedChunks = 0;
    int         status = 0;      // 0=等待, 1=传输中, 2=暂停, 3=完成, 4=失败
    std::string remoteUrl;
    uint64_t    startedAt = 0;
    uint64_t    completedAt = 0;
};

} // namespace core::model

10. 构建配置

10.1 核心库 CMakeLists.txt

# src/core/CMakeLists.txt
add_library(enterprise_core STATIC
    # 网络
    net/connection_mgr.cpp
    net/ws_client.cpp
    net/http_client.cpp
    net/protocol.cpp
    net/heartbeat.cpp
    net/reconnect_strategy.cpp
    net/rate_limiter.cpp
    # 数据
    data/sqlite_store.cpp
    data/pgsql_store.cpp
    data/sync_mgr.cpp
    data/cache_mgr.cpp
    # 业务
    service/im_service.cpp
    service/task_service.cpp
    service/file_service.cpp
    service/report_service.cpp
    # 基础设施
    infra/thread_pool.cpp
    infra/msg_bus.cpp
    infra/config_mgr.cpp
    infra/logger.cpp
    infra/crypto.cpp
    # 数据模型
    model/message.cpp
    model/conversation.cpp
    model/contact.cpp
    model/task.cpp
    model/file_transfer.cpp
)

target_include_directories(enterprise_core PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

target_link_libraries(enterprise_core PUBLIC
    Qt6::Network
    Qt6::WebSockets
    Qt6::Sql
    nlohmann_json::nlohmann_json
    spdlog::spdlog
    SQLite::SQLite3
)

# SQLite 源码直接编译进项目 (或使用系统库)
# target_sources(enterprise_core PRIVATE thirdparty/sqlite3.c)

附录 A — 关键设计指标

模块类数量 (估)接口数量单元测试优先级
net/81 (IConnection)🔴 最高
data/51 (IDataStore)🔴 最高
service/40🟡 中
infra/50🟡 中
gui/15+0🟢 低(人工测试为主)
model/5 (纯结构体)0🟢 低

附录 B — 文档修订记录

版本日期作者变更说明
v0.12025-01初稿,覆盖网络/数据/业务/基础设施/GUI 模块详细设计