竞拍系统案例

wen java案例 1

本文目录导读:

竞拍系统案例

  1. 核心功能设计
  2. 核心竞拍逻辑实现
  3. 系统优化策略
  4. 部署架构
  5. 测试用例示例
  6. 问题与解决方案
  7. 上线与运维

我来为你提供一个完整的竞拍系统案例,从需求分析到技术实现,再到核心业务逻辑。

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 最佳实践

  • 使用消息队列异步处理
  • 配置合理的事务边界
  • 引入幂等性设计
  • 使用乐观锁防止超卖

上线与运维

  1. 灰度发布:小流量验证
  2. 监控指标:QPS、RT、成功率
  3. 应急预案:高并发抢购降温
  4. 数据备份:每日增量备份
  5. 安全加固:WAF、RBAC权限控制

这个案例覆盖了竞拍系统的核心业务流程、技术难点和解决方案,根据实际需求,可以灵活调整功能模块,比如增加推荐系统、社交分享等扩展功能。

抱歉,评论功能暂时关闭!