一、促销活动时间规则设计
1. 时间维度定义
- 基础时间类型:支持按日、周、月、季度、节日(如春节、双十一)等预设周期,或自定义任意时间段(如`2024-06-01 10:00:00`至`2024-06-05 23:59:59`)。
- 时区适配:针对跨国商城,需支持时区转换(如UTC+8转换为用户本地时间),避免因时区差异导致促销提前或延迟生效。
- 重复规则:支持周期性重复(如每周五10:00-22:00)或一次性活动,结合Cron表达式实现灵活调度。
2. 优先级与冲突处理
- 规则优先级:当多个促销活动时间重叠时,按“专属活动 > 全局活动 > 默认价格”的优先级生效,避免价格冲突。
- 冲突预警:在后台管理界面实时检测时间重叠,提示管理员调整规则。
二、系统架构优化(万象源码部署)
1. 数据库设计
- 活动表结构:
```sql
CREATE TABLE promotion_activities (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) NOT NULL,
start_time DATETIME NOT NULL,
end_time DATETIME NOT NULL,
time_zone VARCHAR(50) DEFAULT UTC+8,
repeat_rule VARCHAR(255), -- Cron表达式或周期描述
status TINYINT DEFAULT 1, -- 1:启用, 0:禁用
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
```
- 索引优化:为`start_time`、`end_time`和`status`字段添加复合索引,加速时间范围查询。
2. 缓存策略
- Redis缓存:将活动列表按时间范围分片缓存(如`promotion:active:20240601`),设置TTL为活动结束时间+1小时,避免缓存雪崩。
- 本地缓存:在服务层使用Caffeine缓存当前生效的活动,减少数据库查询。
3. 定时任务调度
- Quartz/Elastic-Job:部署分布式定时任务,每分钟扫描即将生效/失效的活动,更新缓存和状态。
- 事件驱动:通过消息队列(如RocketMQ)触发活动状态变更事件,通知相关服务(如价格计算、库存锁定)。
三、用户界面交互设计
1. 后台管理端
- 可视化时间选择器:集成日期时间组件(如Laydate、Ant Design DatePicker),支持快速选择常见时间段。
- 实时预览:在设置时间后,显示活动生效的本地时间(如“北京时间 2024-06-01 10:00:00”)。
- 批量操作:支持按商品分类、标签批量设置活动时间,提升运营效率。
2. 前端展示
- 倒计时组件:在商品详情页、购物车页显示活动剩余时间(如“距结束还剩2小时30分”),使用`setInterval`动态更新。
- 时间标签:对处于促销中的商品添加“限时特惠”标识,提升用户感知。
四、数据一致性保障
1. 事务控制
- 数据库事务:在创建/更新活动时,确保时间范围、商品关联、价格变更等操作原子性。
- 分布式锁:对高并发场景(如秒杀活动),使用Redis锁防止超卖或时间规则被篡改。
2. 数据校验
- 时间合法性:校验`end_time > start_time`,且不与历史活动完全重叠。
- 时区转换:在保存数据前,将用户输入的本地时间转换为UTC时间存储,避免时区混淆。
3. 监控与告警
- 日志记录:记录活动状态变更、定时任务执行结果等关键操作。
- 异常告警:当活动未按时生效/失效时,通过邮件、短信通知运维人员。
五、部署与测试
1. 灰度发布
- 先在测试环境验证时间规则、缓存更新、定时任务等逻辑,再逐步推广至生产环境。
- 使用A/B测试对比不同时间策略对转化率的影响。
2. 压力测试
- 模拟高并发场景(如每秒1000次活动查询),验证系统响应时间和缓存命中率。
- 测试时区转换、重复规则等边界条件。
六、示例代码(关键逻辑)
```java
// 检查商品是否在促销期内(服务层逻辑)
public boolean isPromotionActive(Long productId, LocalDateTime userLocalTime) {
// 1. 查询缓存中当前生效的活动
String cacheKey = "promotion:active:" + productId;
List
activities = redisTemplate.opsForList().range(cacheKey, 0, -1);
// 2. 若缓存未命中,从数据库查询并更新缓存
if (activities == null || activities.isEmpty()) {
activities = promotionActivityRepository.findByProductIdAndStatus(productId, 1);
for (PromotionActivity activity : activities) {
// 转换时区并检查时间范围
ZonedDateTime startZoned = activity.getStartTime().atZone(ZoneId.of(activity.getTimeZone()));
ZonedDateTime endZoned = activity.getEndTime().atZone(ZoneId.of(activity.getTimeZone()));
ZonedDateTime userZoned = userLocalTime.atZone(ZoneId.systemDefault());
if (userZoned.isAfter(startZoned.withZoneSameInstant(ZoneId.systemDefault()))
&& userZoned.isBefore(endZoned.withZoneSameInstant(ZoneId.systemDefault()))) {
return true;
}
}
// 更新缓存(示例:简单缓存,实际需结合过期策略)
redisTemplate.opsForList().rightPushAll(cacheKey, activities);
}
return false;
}
```
总结
通过精细化的时间规则设计、高可用的系统架构、用户友好的交互界面、严格的数据一致性保障,可实现水果商城促销活动的精准控制。万象源码部署时,需结合具体技术栈(如Spring Boot+MyBatis+Redis)调整实现细节,并重点测试时区转换、高并发场景下的稳定性。