本文目录导读:

我来为你提供一个完整的竞拍系统案例,从需求分析到技术实现,再到核心业务逻辑。
1 项目背景
构建一个在线竞拍系统,支持用户注册、商品发布、实时竞价、自动延长、成交管理等功能。
2 技术栈选择
- 后端:Java Spring Boot / Node.js
- 前端:Vue.js / React
- 数据库:MySQL + Redis
- 消息队列:RabbitMQ / Kafka
- 实时通信:WebSocket
核心功能设计
1 核心业务模块
竞拍系统
├── 用户模块
│ ├── 注册/登录
│ ├── 身份认证
│ └── 信用评分
├── 商品模块
│ ├── 商品发布
│ ├── 商品展示
│ └── 商品审核
├── 竞拍模块
│ ├── 出价功能
│ ├── 代理出价
│ └── 自动延长
├── 结算模块
│ ├── 订单生成
│ ├── 支付系统
│ └── 物流管理
└── 管理模块
├── 系统监控
├── 风控管理
└── 数据分析
2 数据库设计
-- 商品表
CREATE TABLE auction_items (
id BIGINT PRIMARY KEY AUTO_INCREMENT,VARCHAR(200) NOT NULL,
description TEXT,
starting_price DECIMAL(10,2) NOT NULL,
current_price DECIMAL(10,2),
min_increment DECIMAL(10,2) DEFAULT 1.00,
start_time DATETIME NOT NULL,
end_time DATETIME NOT NULL,
seller_id BIGINT NOT NULL,
status TINYINT COMMENT '0-待审核 1-进行中 2-已结束 3-已流拍 4-已成交',
bid_count INT DEFAULT 0,
winner_id BIGINT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_status_time (status, end_time),
INDEX idx_seller_id (seller_id)
);
-- 竞价记录表
CREATE TABLE bids (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
item_id BIGINT NOT NULL,
bidder_id BIGINT NOT NULL,
bid_amount DECIMAL(10,2) NOT NULL,
bid_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
is_auto_bid BOOLEAN DEFAULT FALSE,
status TINYINT COMMENT '1-有效 2-被超越 3-已撤回',
INDEX idx_item_time (item_id, bid_time DESC)
);
-- 用户账户表
CREATE TABLE user_accounts (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT NOT NULL,
balance DECIMAL(12,2) DEFAULT 0,
frozen_amount DECIMAL(12,2) DEFAULT 0,
credit_score INT DEFAULT 100,
UNIQUE KEY uk_user_id (user_id)
);
核心竞拍逻辑实现
1 出价逻辑(Java实现)
@Service
public class AuctionService {
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private RabbitTemplate rabbitTemplate;
@Transactional
public BidResult placeBid(BidRequest request) {
// 1. 校验竞拍资格
validateBidder(request);
// 2. 使用分布式锁防止并发问题
String lockKey = "auction:lock:" + request.getItemId();
RLock lock = redissonClient.getLock(lockKey);
try {
if (lock.tryLock(10, TimeUnit.SECONDS)) {
// 3. 检查商品状态和出价时间
AuctionItem item = getAuctionItem(request.getItemId());
checkAuctionRules(item, request.getBidAmount());
// 4. 处理代理出价
BigDecimal bidAmount = handleAutoBidding(item, request);
// 5. 更新商品当前价格
updateCurrentPrice(item.getId(), bidAmount);
// 6. 保存竞价记录
Bid bid = saveBid(request, bidAmount);
// 7. 发送通知
sendBidNotification(item, bidAmount);
// 8. 异步更新排行榜
updateLeaderBoard(item.getId(), bidderId, bidAmount);
return buildSuccessResult(bid);
}
} catch (Exception e) {
throw new AuctionException("Bid failed: " + e.getMessage());
} finally {
lock.unlock();
}
return buildFailureResult();
}
private BigDecimal handleAutoBidding(AuctionItem item, BigDecimal userBid) {
// 代理出价逻辑
// 如果用户设置了最高价,根据最小加价幅度自动加价
BigDecimal nextBid = item.getCurrentPrice().add(item.getMinIncrement());
if (userBid.compareTo(item.getCurrentPrice().add(item.getMinIncrement())) >= 0) {
return userBid;
}
// 检查是否有其他竞拍者的代理价高于此出价
BigDecimal highestAutoBid = getHighestAutoBid(item.getId());
if (highestAutoBid != null && highestAutoBid.compareTo(userBid) >= 0) {
return userBid.add(item.getMinIncrement());
}
return nextBid;
}
private void checkAuctionRules(AuctionItem item, BigDecimal bidAmount) {
// 验证竞拍时间
LocalDateTime now = LocalDateTime.now();
if (now.isBefore(item.getStartTime()) || now.isAfter(item.getEndTime())) {
throw new AuctionException("不在竞拍时间范围内");
}
// 验证出价金额
BigDecimal minBid = item.getCurrentPrice().add(item.getMinIncrement());
if (bidAmount.compareTo(minBid) < 0) {
throw new AuctionException("出价低于最低加价幅度");
}
// 验证用户资金是否充足
if (bidAmount.compareTo(getUserFrozenAmount(currentUserId)) > 0) {
throw new AuctionException("账户余额不足");
}
}
}
2 竞拍时效处理
@Component
public class AuctionScheduler {
@Autowired
private AuctionService auctionService;
// 竞拍结束倒计时任务
public void scheduleAuctionEnd(Long itemId, LocalDateTime endTime) {
long delay = Duration.between(LocalDateTime.now(), endTime).toMillis();
if (delay > 0) {
// 使用延迟队列触发竞拍结束
rabbitTemplate.convertAndSend(
"auction.exchange",
"auction.end",
itemId,
message -> {
message.getMessageProperties().setDelay(delay);
return message;
}
);
}
}
// 自动延长逻辑
private void checkAndExtendAuction(AuctionItem item, LocalDateTime bidTime) {
// 最后5分钟有人出价则延长10分钟
LocalDateTime threshold = item.getEndTime().minusMinutes(5);
if (bidTime.isAfter(threshold)) {
item.setEndTime(item.getEndTime().plusMinutes(10));
auctionService.save(item);
// 通知所有参与者竞拍时间已延长
notifyAuctionExtended(item);
}
}
}
3 实时通信(WebSocket)
// 前端实时竞拍
class AuctionWebSocket {
constructor(itemId, userId) {
this.itemId = itemId;
this.userId = userId;
this.websocket = null;
this.reconnectTimes = 0;
this.connect();
}
connect() {
const protocol = location.protocol === 'https:' ? 'wss' : 'ws';
this.websocket = new WebSocket(`${protocol}://${location.host}/auction/${this.itemId}`);
this.websocket.onmessage = (event) => {
const data = JSON.parse(event.data);
this.handleMessage(data);
};
this.websocket.onclose = () => {
if (this.reconnectTimes < 5) {
setTimeout(() => {
this.reconnectTimes++;
this.connect();
}, 3000 * this.reconnectTimes);
}
};
}
handleMessage(data) {
switch(data.type) {
case 'NEW_BID':
this.updateCurrentPrice(data);
break;
case 'PRICE_CHANGE':
this.updatePriceDisplay(data.price);
break;
case 'END_AUCTION':
this.showAuctionResult(data.winner);
break;
case 'EXTEND_AUCTION':
this.updateTimer(data.newEndTime);
break;
case 'NOTIFICATION':
this.showNotification(data.message);
break;
}
}
sendBid(amount) {
this.websocket.send(JSON.stringify({
type: 'PLACE_BID',
itemId: this.itemId,
amount: amount
}));
}
}
系统优化策略
1 性能优化
- Redis缓存:商品列表、用户信息、竞拍状态
- 动态分页:大数据量商品页面加载优化
- Nginx负载均衡:解决高并发问题
2 安全性保障
- 防重复出价:使用Redis SETNX
- 限流措施:接口调用频率限制
- 订单防篡改:签名机制和幂等性保证
3 监控告警
// 监控指标设计
public class AuctionMetrics {
// JVM监控
Gauge jvmMemoryUsed = new Gauge("jvm_memory_used", "JVM内存使用量");
// 接口监控
Counter bidRequestCount = new Counter("bid_request_total", "出价请求总数");
// 竞拍成功率
Gauge auctionSuccessRate = new Gauge(
"auction_success_rate",
"竞拍成功率"
);
// 预警规则
AlertRule highLatencyRule = new AlertRule()
.metric("bid_latency")
.threshold(500, TimeUnit.MILLISECONDS)
.window(1, TimeUnit.MINUTES);
}
部署架构
┌──────────────────────────────────────────┐
│ CDN / 负载均衡 │
├──────────────────────────────────────────┤
│ Nginx (Web Server) │
├──────────────┬───────────────────────────┤
│ 前端集群 │ 后端集群 │
│ Vue/React │ Spring Boot (多实例) │
├──────────────┴───────────────────────────┤
│ Redis集群 / RabbitMQ │
├──────────────────────────────────────────┤
│ MySQL主从 / MongoDB │
├──────────────────────────────────────────┤
│ 监控系统 / 日志系统 │
└──────────────────────────────────────────┘
测试用例示例
@SpringBootTest
class AuctionServiceTest {
@Test
void testBidding() {
// 测试竞拍成功
BidRequest request = buildTestBidRequest();
BidResult result = auctionService.placeBid(request);
assertNotNull(result);
assertTrue(result.isSuccess());
assertEquals(expectedPrice, result.getCurrentPrice());
}
@Test
void testConcurrentBidding() {
// 模拟并发出价
ExecutorService executor = Executors.newFixedThreadPool(100);
CountDownLatch latch = new CountDownLatch(100);
for (int i = 0; i < 100; i++) {
executor.submit(() -> {
try {
placeBid(i);
} finally {
latch.countDown();
}
});
}
latch.await(10, TimeUnit.SECONDS);
// 验证最终价格正确
assertEquals(currentExpectedPrice, auctionItem.getCurrentPrice());
}
@Test
void testValidation() {
// 测试竞拍开始前不允许出价
when(now).thenReturn(startTime.minusSeconds(1));
assertThrows(AuctionException.class, () -> {
auctionService.placeBid(validRequest);
});
}
}
问题与解决方案
1 常见问题
- 并发竞价冲突
- 拍卖时间不一致
- 支付延迟
- 竞拍取消处理
2 解决方案
并发控制:
// Redis分布式锁 + 乐观锁
public BidResult placeBid(BidRequest request) {
// 使用Redis分布式锁
String lockKey = "lock:auction:" + request.getItemId();
Boolean acquired = redisTemplate.opsForValue()
.setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS);
if (!acquired) {
return new BidResult(ResultCode.BUSY);
}
try {
// 业务处理...
} finally {
redisTemplate.delete(lockKey);
}
}
3 最佳实践
- 使用消息队列异步处理
- 配置合理的事务边界
- 引入幂等性设计
- 使用乐观锁防止超卖
上线与运维
- 灰度发布:小流量验证
- 监控指标:QPS、RT、成功率
- 应急预案:高并发抢购降温
- 数据备份:每日增量备份
- 安全加固:WAF、RBAC权限控制
这个案例覆盖了竞拍系统的核心业务流程、技术难点和解决方案,根据实际需求,可以灵活调整功能模块,比如增加推荐系统、社交分享等扩展功能。