功能概述
小象买菜系统的动态价格展示功能需要根据市场行情、库存情况、促销活动等因素实时更新商品价格,为用户提供准确的价格信息。
技术实现方案
1. 系统架构设计
```
前端展示层 → API网关 → 价格服务 → 缓存层 → 数据库
↑
定时任务/消息队列
```
2. 核心实现步骤
2.1 数据库设计
```sql
-- 商品基础表
CREATE TABLE products (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) NOT NULL,
base_price DECIMAL(10,2) NOT NULL,
category_id BIGINT,
-- 其他基础字段
);
-- 价格历史表
CREATE TABLE price_history (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
product_id BIGINT NOT NULL,
price DECIMAL(10,2) NOT NULL,
start_time DATETIME NOT NULL,
end_time DATETIME,
source VARCHAR(50), -- 手动调整/自动调价/促销活动等
operator VARCHAR(50),
FOREIGN KEY (product_id) REFERENCES products(id)
);
-- 实时价格表(可考虑使用Redis)
CREATE TABLE current_prices (
product_id BIGINT PRIMARY KEY,
current_price DECIMAL(10,2) NOT NULL,
update_time DATETIME NOT NULL,
FOREIGN KEY (product_id) REFERENCES products(id)
);
```
2.2 后端服务实现
价格计算服务(Java示例)
```java
@Service
public class PriceService {
@Autowired
private ProductRepository productRepository;
@Autowired
private PriceHistoryRepository priceHistoryRepository;
@Autowired
private RedisTemplate
redisTemplate;
// 获取商品当前价格(带缓存)
public BigDecimal getCurrentPrice(Long productId) {
// 1. 先从Redis缓存获取
String cacheKey = "price:" + productId;
BigDecimal cachedPrice = (BigDecimal) redisTemplate.opsForValue().get(cacheKey);
if (cachedPrice != null) {
return cachedPrice;
}
// 2. 缓存未命中,从数据库获取
CurrentPrice currentPrice = priceHistoryRepository.findLatestPrice(productId);
if (currentPrice != null) {
// 3. 更新缓存,设置较短过期时间(如5分钟)
redisTemplate.opsForValue().set(cacheKey, currentPrice.getCurrentPrice(),
5, TimeUnit.MINUTES);
return currentPrice.getCurrentPrice();
}
// 4. 回退到基础价格
Product product = productRepository.findById(productId)
.orElseThrow(() -> new RuntimeException("Product not found"));
return product.getBasePrice();
}
// 更新价格(由定时任务或管理员调用)
@Transactional
public void updatePrice(Long productId, BigDecimal newPrice, String source) {
// 1. 记录价格历史
PriceHistory history = new PriceHistory();
history.setProductId(productId);
history.setPrice(newPrice);
history.setStartTime(LocalDateTime.now());
history.setSource(source);
priceHistoryRepository.save(history);
// 2. 更新当前价格
CurrentPrice current = new CurrentPrice();
current.setProductId(productId);
current.setCurrentPrice(newPrice);
current.setUpdateTime(LocalDateTime.now());
// 这里简化处理,实际可能需要先删除旧记录再插入
// 3. 更新缓存
String cacheKey = "price:" + productId;
redisTemplate.opsForValue().set(cacheKey, newPrice);
}
}
```
2.3 动态价格触发机制
定时调价任务(Spring Scheduler示例)
```java
@Component
public class PriceAdjustmentScheduler {
@Autowired
private PriceService priceService;
@Autowired
private MarketDataService marketDataService;
// 每天凌晨3点执行市场价格同步
@Scheduled(cron = "0 0 3 * * ?")
public void syncMarketPrices() {
List products = productRepository.findAll();
for (Product product : products) {
// 获取市场参考价(可从第三方API或内部算法获取)
BigDecimal marketPrice = marketDataService.getMarketPrice(product.getCategoryId());
// 简单调价逻辑:基础价*市场波动系数
double adjustmentFactor = 0.95 + Math.random() * 0.1; // 示例随机波动
BigDecimal newPrice = product.getBasePrice()
.multiply(BigDecimal.valueOf(adjustmentFactor))
.setScale(2, RoundingMode.HALF_UP);
// 更新价格(来源标记为"market_adjustment")
priceService.updatePrice(product.getId(), newPrice, "market_adjustment");
}
}
// 库存低于阈值时触发调价
@EventListener
public void handleLowStockEvent(LowStockEvent event) {
Long productId = event.getProductId();
BigDecimal currentPrice = priceService.getCurrentPrice(productId);
// 库存紧张时提高价格(示例逻辑)
BigDecimal newPrice = currentPrice.multiply(BigDecimal.valueOf(1.1))
.setScale(2, RoundingMode.HALF_UP);
priceService.updatePrice(productId, newPrice, "low_stock_adjustment");
}
}
```
2.4 前端展示实现
Vue.js组件示例
```javascript
{{ product.name }}
¥{{ currentPrice.toFixed(2) }}
¥{{ originalPrice.toFixed(2) }}
<script>
export default {
props: [productId],
data() {
return {
product: {},
currentPrice: 0,
originalPrice: null,
pollInterval: null
};
},
created() {
this.fetchProductData();
// 每30秒轮询价格
this.pollInterval = setInterval(this.fetchPrice, 30000);
},
beforeDestroy() {
clearInterval(this.pollInterval);
},
methods: {
async fetchProductData() {
const response = await axios.get(`/api/products/${this.productId}`);
this.product = response.data;
this.fetchPrice();
},
async fetchPrice() {
const response = await axios.get(`/api/prices/${this.productId}/current`);
this.currentPrice = response.data.price;
// 如果有促销价,显示原价划掉效果
if (response.data.originalPrice) {
this.originalPrice = response.data.originalPrice;
}
}
}
};
<style>
.price-display {
font-size: 1.2em;
margin: 10px 0;
}
.current-price {
font-weight: bold;
color: e74c3c;
}
.original-price {
text-decoration: line-through;
color: 95a5a6;
margin-left: 10px;
}
```
3. 高级功能扩展
1. 实时价格推送(WebSocket)
- 使用WebSocket实现价格实时更新,避免轮询
- 前端连接价格更新频道,后端在价格变动时主动推送
2. 个性化定价
- 基于用户历史购买行为、会员等级等实施差异化定价
- 在价格计算服务中加入用户维度参数
3. 价格预测与预警
- 使用机器学习模型预测价格走势
- 当预测价格将大幅波动时提前通知运营人员
4. 多级缓存策略
- 本地缓存(如Guava)→ Redis → 数据库
- 设置不同的过期时间和更新策略
部署与监控
1. 监控指标
- 价格更新延迟
- 缓存命中率
- 价格计算服务响应时间
2. 告警机制
- 价格长时间未更新告警
- 价格异常波动告警
3. 日志记录
- 详细记录每次价格变动原因
- 记录价格计算过程中的关键参数
测试方案
1. 单元测试
- 测试各种价格计算场景
- 测试缓存更新逻辑
2. 集成测试
- 测试价格服务与数据库、缓存的交互
- 测试定时任务执行情况
3. 性能测试
- 模拟高并发价格查询
- 测试缓存穿透/雪崩场景
实施建议
1. 初期可采用简单的定时调价策略,逐步引入更复杂的动态定价算法
2. 确保价格变动有完整的审计日志
3. 考虑价格展示的用户体验,如价格变动动画、历史价格走势图等
4. 实现价格锁定期功能,防止价格频繁变动影响用户体验
通过以上方案,小象买菜系统可以实现高效、准确的动态价格展示功能,提升用户购物体验和平台运营效率。