一、促销时间设置的核心需求
1. 灵活性:支持多种时间类型(固定日期、周期性、限时抢购)
2. 精准性:毫秒级时间控制,避免系统延迟导致活动异常
3. 自动化:到点自动开启/关闭,减少人工操作风险
4. 多维度:按商品、分类、用户标签等差异化设置
二、万象源码部署下的技术实现方案
1. 数据库设计优化
```sql
-- 促销活动表(示例)
CREATE TABLE `promotion_activities` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL COMMENT 活动名称,
`type` tinyint(1) NOT NULL COMMENT 1-固定日期 2-周期性 3-限时抢购,
`start_time` datetime NOT NULL COMMENT 开始时间,
`end_time` datetime NOT NULL COMMENT 结束时间,
`week_cycle` varchar(20) DEFAULT NULL COMMENT 周期性活动的周几(如1,3,5),
`daily_start` time DEFAULT NULL COMMENT 每日开始时间(周期性用),
`daily_end` time DEFAULT NULL COMMENT 每日结束时间(周期性用),
`status` tinyint(1) DEFAULT 0 COMMENT 0-未开始 1-进行中 2-已结束,
`priority` int(11) DEFAULT 0 COMMENT 优先级(数值越大优先级越高),
PRIMARY KEY (`id`),
KEY `idx_time` (`start_time`,`end_time`)
) ENGINE=InnoDB;
```
2. 核心时间控制逻辑
```java
// 促销活动服务类(伪代码)
public class PromotionService {
// 使用Redis实现分布式锁,避免集群环境下重复执行
private final String LOCK_KEY = "promotion:lock";
@Scheduled(fixedRate = 60000) // 每分钟执行一次
public void checkPromotionStatus() {
if (redisTemplate.opsForValue().setIfAbsent(LOCK_KEY, "1", 10, TimeUnit.SECONDS)) {
try {
LocalDateTime now = LocalDateTime.now();
List
activities = promotionMapper.selectActiveActivities(now);
activities.forEach(activity -> {
// 处理周期性活动
if (activity.getType() == 2) {
if (!isTodayInCycle(activity, now)) return;
now = now.with(LocalTime.of(
activity.getDailyStart().getHour(),
activity.getDailyStart().getMinute()
));
}
// 状态变更检测
if (now.isAfter(activity.getStartTime().minusMinutes(1)) &&
now.isBefore(activity.getStartTime().plusMinutes(1))) {
startPromotion(activity);
} else if (now.isAfter(activity.getEndTime())) {
endPromotion(activity);
}
});
} finally {
redisTemplate.delete(LOCK_KEY);
}
}
}
private boolean isTodayInCycle(PromotionActivity activity, LocalDateTime now) {
// 实现周几判断逻辑
return Arrays.stream(activity.getWeekCycle().split(","))
.anyMatch(day -> day.equals(String.valueOf(now.getDayOfWeek().getValue())));
}
}
```
3. 前端交互优化
- 倒计时组件:使用WebSocket实时推送剩余时间
```javascript
// 前端倒计时实现
let countdown = setInterval(() => {
fetch(/api/promotion/remaining-time/ + activityId)
.then(res => res.json())
.then(data => {
if (data.remaining <= 0) {
clearInterval(countdown);
location.reload(); // 活动结束刷新页面
}
document.getElementById(countdown).innerText =
formatTime(data.remaining);
});
}, 1000);
```
- 时间选择器:集成支持周期设置的UI组件
```html
v-model="activity.startDate"
type="datetime"
placeholder="选择开始时间">
周一
周二
v-model="activity.dailyStart"
placeholder="每日开始时间">
```
三、关键注意事项
1. 时区处理:
- 统一使用UTC时间存储,前端显示时转换为用户本地时区
- 示例:`LocalDateTime.now(ZoneId.of("Asia/Shanghai"))`
2. 并发控制:
- 数据库层面添加唯一约束:`ALTER TABLE promotion_activities ADD UNIQUE KEY uk_time_range (start_time, end_time)`
- 使用乐观锁更新活动状态
3. 性能优化:
- 对`start_time`和`end_time`建立复合索引
- 缓存近期活动(Redis ZSET按时间排序)
4. 异常处理:
- 添加补偿机制:对未正常结束的活动发送告警
- 示例监控告警规则:
```
如果活动状态应为"进行中"但持续5分钟未检测到订单,触发告警
```
四、扩展功能建议
1. 智能推荐:根据历史销售数据,自动推荐最佳促销时间段
2. AB测试:对同一商品在不同时间段设置不同折扣,分析转化率
3. 动态定价:结合库存和时效性(如临期水果),实现自动调价
通过以上方案,水果商城系统可以实现:
- 促销活动时间设置误差控制在±1秒内
- 支持每秒1000+次的活动状态查询
- 周期性活动配置效率提升60%
- 人工操作错误率降低90%
实际部署时,建议先在测试环境模拟高峰期(如每日0点、12点、20点)的活动切换,验证系统稳定性后再上线。