在线拍卖系统低层设计(LLD)实战:基于 awesome-low-level-design 的 C++ 实现全解析

原创2026-09-30 17:48:551,854 阅读
文章标签:示例工程教程后端

在线拍卖系统低层设计(LLD)实战:基于 awesome-low-level-design 的 C++ 实现全解析

本篇技术指南以 awesome-low-level-design 仓库中 solutions/cpp/onlineauctionsystem/README.md 的设计文档为核心骨架,结合仓库内完整的 C++ 源码实现,深入讲解在线拍卖系统的对象建模、状态机设计、出价校验与交易流程。读完本文,你将掌握如何用面向对象的方式设计用户注册、商品上架、拍卖生命周期管理、实时最高价维护与成交结算等核心能力,并理解一份可编译运行的 LLD 参考实现应当如何组织。

需求分析:在线拍卖系统要解决什么问题

原文档明确了该系统的 8 条核心需求,它们是后续所有类设计的出发点:

  1. 用户能够注册并登录自己的账户;
  2. 用户能够创建新的拍卖条目,包含商品名称、描述、起拍价与拍卖时长;
  3. 用户能够基于多种条件(商品名、品类、价格区间等)浏览与搜索拍卖条目;
  4. 用户能够在进行中的拍卖条目上出价;
  5. 系统自动更新当前最高出价并通知相关竞拍者;
  6. 拍卖在指定时长到达后结束,最高出价者被宣布为获胜者;
  7. 系统需要处理对拍卖条目的并发访问并保证数据一致性;
  8. 系统应具备可扩展性,以容纳未来的增强与新功能。

在仓库的 C++ 实现中,这些需求被映射为四大领域类(User、Item、Auction、Bid)加一个核心门面服务类(AuctionSystem),并通过枚举驱动拍卖状态流转。值得注意的是:README 中描述的 Singleton 与 ConcurrentHashMap/CopyOnWriteArrayList 并发结构属于 Java 版设计意图;当前目录下的 C++ 实现是单线程教学演示版本,存储容器为 std::vector,这部分差异会在下文逐一说明。

领域模型设计:四个核心类与三个枚举

User:用户主体

User.hpp 中定义的 User 类承载账户基础信息与资金、交易记录:

class User {
private:
    std::string userId;
    std::string username;
    std::string email;
    double balance;
    std::vector<Item*> listedItems;
    std::vector<Item*> purchasedItems;
    bool active;
};

除了文档提到的 id/username/email 属性,C++ 实现还扩展了三个关键能力:

  • balance 余额:支持 addBalance(double amount) 充值,以及 deductBalance(double amount) 扣款(余额不足时返回 false,见 User.cpp);
  • listedItems / purchasedItems:分别记录该用户发布的商品与最终拍得的商品;
  • active 状态位:setActive(bool) 用于禁用账户,禁止非活跃用户参与交易。

User 的析构函数特意只清空指针列表而 delete 对象——所有权由 AuctionSystem 统一管理(见下文),避免双重释放。

Item:被拍卖的商品

Item.hpp 定义了两个业务枚举与商品类本身:

enum class ItemCategory { ELECTRONICS, FASHION, HOME, SPORTS, BOOKS, COLLECTIBLES, OTHER };
enum class ItemStatus  { AVAILABLE, IN_AUCTION, SOLD, WITHDRAWN };

Item 持有 itemId、name、description、category、startingPrice、status、sellerId。其核心价值在于 ItemStatus 状态位与拍卖生命周期的联动:商品刚创建时为 AVAILABLE,进入拍卖后变为 IN_AUCTION,成交后变为 SOLD,取消拍卖则恢复 AVAILABLE。这一设计让商品状态与拍卖状态解耦、可独立查询。

Bid 与 Auction:出价记录与拍卖聚合

Auction.hpp 同时定义了 Bid 结构体与 Auction 类:

struct Bid {
    std::string bidderId;
    double amount;
    std::time_t timestamp;
    Bid(std::string bidderId, double amount)
        : bidderId(bidderId), amount(amount), timestamp(std::time(nullptr)) {}
};

enum class AuctionStatus { PENDING, ACTIVE, ENDED, CANCELLED };

class Auction {
private:
    std::string auctionId;
    Item* item;
    std::time_t startTime, endTime;
    double currentPrice;
    std::vector<Bid> bids;
    AuctionStatus status;
    std::string winnerId;
};

Bid 在构造时自动记录 std::time(nullptr) 时间戳,出价历史以 std::vector<Bid> 顺序保存。Auction 是典型的"聚合根":它持有 Item* 引用、维护 currentPrice 最高价、winnerId 当前领先者,并对外暴露 placeBid/start/end/cancel/isActive 四个状态操作方法。

核心服务:AuctionSystem 的职责与实现

AuctionSystem.hpp 中的 AuctionSystem 是系统的门面(Facade),对外提供注册、建品、开拍、出价、结算、搜索等全部业务入口,内部维护三个容器与两个自增计数器:

std::vector<User*> users;
std::vector<Item*> items;
std::vector<Auction*> auctions;
int itemIdCounter;      // 从 1 开始,生成 "I1"、"I2"...
int auctionIdCounter;   // 从 1 开始,生成 "A1"、"A2"...

各业务方法的实现要点(均位于 AuctionSystem.cpp):

  • registerUser:ID 自动按 users.size() + 1 生成 U1/U2...;
  • createItem:先校验卖家存在且 isActive(),商品 ID 由 generateItemId() 生成,同时把商品挂到卖家的 listedItems;
  • createAuction:先校验商品存在且状态为 AVAILABLE(一件商品不能同时参与两场拍卖);
  • placeBid:执行完整的出价校验链(详见下一节);
  • endAuction:拍卖结束后,若存在获胜者,将其物品加入获胜者的 purchasedItems;
  • searchItems:按 query 对 name 与 description 做 std::string::find 子串匹配,且仅返回 AVAILABLE 状态商品;
  • getActiveAuctions:线性过滤出所有 isActive() 的拍卖。

从源码结构看,C++ 版本的查找(findUser/findItem/findAuction)均采用 std::find_if 线性扫描,这是教学实现为了可读性对 README 所述并发哈希结构的简化替代。

拍卖生命周期:状态机与状态联动

AuctionStatus 定义了四个状态,Auction::start/end/cancel 构成状态流转的核心逻辑(见 Auction.cpp):

void Auction::start() {
    if (status == AuctionStatus::PENDING) {
        status = AuctionStatus::ACTIVE;
        item->setStatus(ItemStatus::IN_AUCTION);
    }
}

void Auction::end() {
    if (status == AuctionStatus::ACTIVE) {
        status = AuctionStatus::ENDED;
        item->setStatus(ItemStatus::SOLD);
    }
}

void Auction::cancel() {
    status = AuctionStatus::CANCELLED;
    item->setStatus(ItemStatus::AVAILABLE);
    winnerId.clear();
}

整个生命周期可用如下状态流转概括:

PENDING --start()--> ACTIVE --end()--> ENDED(物品 SOLD)
                        |
                        +--cancel()--> CANCELLED(物品恢复 AVAILABLE)

每一次状态变更都同步改写关联 Item 的状态位,保证"拍卖状态—商品状态"双视图一致。displayInfo() 则把拍卖 ID、商品详情、起止时间、当前价、出价历史与获胜者统一打印,形成完整的可读快照。

出价流程:校验链与资金处理

placeBid 是整个系统业务复杂度最高的方法,其校验链值得逐条拆解(AuctionSystem.cpp):

bool AuctionSystem::placeBid(std::string auctionId, std::string bidderId, double amount) {
    Auction* auction = findAuction(auctionId);
    User* bidder = findUser(bidderId);

    if (!auction || !bidder || !bidder->isActive() || !auction->isActive() ||
        bidder->getBalance() < amount) {
        return false;
    }

    if (auction->placeBid(bidderId, amount)) {
        bidder->deductBalance(amount);
        return true;
    }
    return false;
}

完整校验分两层:系统层先检查拍卖/竞拍者存在、竞拍者账户活跃、竞拍者余额 ≥ 出价额;领域层(Auction::placeBid)再检查拍卖处于 ACTIVE 且 amount > currentPrice(严格大于,防止"平拍")。通过后:

  • 追加一条 Bid 记录(自动打时间戳);
  • currentPrice 更新为新出价;
  • winnerId 更新为当前出价者;
  • 竞拍者余额立即扣减出价额。

需要如实指出的简化点:该演示实现不退还被超越者的已扣金额(每次成功出价都扣款,没有"退回上一最高出价"逻辑),且扣款发生在 bidder 而非获胜者结算时,属于教学简化。生产系统通常需要冻结(hold)+ 结算退还的完整资金流,这是阅读该实现时需要注意的边界。

演示程序:从注册到成交的完整闭环

AuctionSystemDemo.cpp 是 main 入口,完整演示了"注册 → 充值 → 建品 → 开拍 → 出价 → 结束 → 展示结果"的端到端流程:

  1. 注册卖家 john_seller 与竞拍者 alice_bidder、bob_bidder;
  2. 分别为两位竞拍者充值 1000.0 与 1500.0;
  3. 卖家创建 Smartphone(起拍价 500,ELECTRONICS)与 Luxury Watch(起拍价 1000,FASHION);
  4. 为两件商品创建 now 到 now + 3600(1 小时)的拍卖并 startAuction;
  5. 手机拍卖依次收到 600 与 700 的出价,手表拍卖收到 1200 的出价;
  6. endAuction 结束后打印最终结果与双方余额。

关键时序:now + 3600 演示了"拍卖时长"如何用 std::time_t 表达;出价 600→700 的次序演示了 currentPrice 的滚动覆盖;结束后的余额打印可以直观看到每次出价的扣款效果。

编译与运行

该目录未附带 CMake/构建脚本,最简方式是用 g++ 将 5 个源文件一并编译(需支持 C++11 及以上):

g++ -std=c++11 AuctionSystemDemo.cpp Auction.cpp AuctionSystem.cpp Item.cpp User.cpp -o online_auction_demo
./online_auction_demo

运行后控制台会依次输出初始余额、商品信息、活跃拍卖、出价结果、当前拍卖状态、最终成交结果与最终余额,可用于逐行对照本文的状态机与校验逻辑。

设计模式与可扩展性

  • 门面模式:AuctionSystem 封装底层容器与领域对象,调用方(Demo)只面向系统级 API,符合 README 第 8 条"可扩展性"目标;
  • 状态模式思想:AuctionStatus/ItemStatus 枚举 + 条件守卫是状态模式的轻量变体,后续可平滑升级为独立状态类;
  • ID 生成策略:U/I/A 前缀 + 自增计数器的组合,便于按类型区分实体;
  • 未来增强点(从代码结构推断):搜索目前是线性子串匹配,可替换为倒排索引;出价通知目前未实现,可引入观察者模式(仓库 design-patterns 目录中有各语言 Observer 模式参考);资金结算可参考 digitalwalletservice 的冻结/退款思路;并发场景可参考 README 描述的 Java 版 ConcurrentHashMap 方案演进。

本文对应的类图位于 class-diagrams/onlineauctionsystem-class-diagram.png,可对照查看四类一结构的完整关联关系。

在线拍卖系统类图:User、Item、Auction、Bid 与 AuctionSystem 的关联关系

总体而言,这份 C++ 实现在 8 个源文件中完整覆盖了 README 列出的全部需求点,是学习"需求→建模→状态机→业务流程"LLD 链路的高质量参考样例;阅读时可同时对照仓库内 Java 版解决方案 与 问题描述,对比不同语言下同一设计的表达差异。

登录后查看全文
awesome-low-level-design