alien-entity/src/main/java/shop/alien/entity/store/StoreTrackEvent.java - 埋点事件实体类alien-entity/src/main/java/shop/alien/entity/store/StoreTrackStatistics.java - 埋点统计数据实体类alien-store/src/main/java/shop/alien/store/annotation/TrackEvent.java - 埋点注解alien-store/src/main/java/shop/alien/store/service/TrackEventService.java - 埋点事件服务接口alien-store/src/main/java/shop/alien/store/controller/TrackEventController.java - 埋点上报接口alien-store/doc/埋点需求完整方案.md - 完整方案文档文件路径: alien-store/src/main/java/shop/alien/store/aspect/TrackEventAspect.java
功能: 拦截标注了@TrackEvent注解的方法,自动收集埋点数据并写入Redis List
关键代码要点:
@Around环绕通知storeId、userId等参数BaseRedisService.setListRight()写入Redis List@Order注解设置切面执行顺序文件路径: alien-store/src/main/java/shop/alien/store/service/impl/TrackEventServiceImpl.java
功能:
saveTrackEvent()方法,将埋点数据写入Redis ListbatchSaveTrackEvents()方法,批量保存到数据库getBusinessData()方法,统计查询经营数据compareBusinessData()方法,对比数据calculateAndSaveStatistics()方法,计算统计数据getPriceRankingData()方法,获取价目表排名文件路径: alien-store/src/main/java/shop/alien/store/service/TrackEventConsumer.java
功能: 定时任务,从Redis List批量消费数据并写入数据库
关键代码要点:
@Scheduled(cron = "0/10 * * * * ?")每10秒执行一次文件路径: alien-store/src/main/java/shop/alien/mapper/StoreTrackEventMapper.java
文件路径: alien-store/src/main/java/shop/alien/mapper/StoreTrackStatisticsMapper.java
文件路径: alien-store/src/main/java/shop/alien/store/controller/BusinessDataController.java
接口列表:
GET /business/data - 查询经营数据GET /business/data/compare - 数据对比GET /business/data/history - 历史数据查询文件路径: alien-store/src/main/java/shop/alien/store/controller/AIRecoveryController.java
接口列表:
GET /business/ai/recommendation - 获取AI推荐@Slf4j
@Aspect
@Component
@Order(2)
@RequiredArgsConstructor
public class TrackEventAspect {
private final BaseRedisService baseRedisService;
private final SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
private final DefaultParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
private static final String REDIS_QUEUE_KEY = "track:event:queue";
@Around("@annotation(trackEvent)")
public Object around(ProceedingJoinPoint joinPoint, TrackEvent trackEvent) throws Throwable {
Object result = joinPoint.proceed();
// 构建埋点事件对象
StoreTrackEvent event = buildTrackEvent(joinPoint, trackEvent);
// 异步写入Redis List
if (trackEvent.async()) {
baseRedisService.setListRight(REDIS_QUEUE_KEY, JSON.toJSONString(event));
} else {
// 同步写入
trackEventService.batchSaveTrackEvents(Collections.singletonList(event));
}
return result;
}
private StoreTrackEvent buildTrackEvent(ProceedingJoinPoint joinPoint, TrackEvent annotation) {
// 解析SpEL表达式获取参数值
// 获取用户ID、店铺ID等
// 设置IP、User-Agent等
// ...
}
}
@Component
@RequiredArgsConstructor
@Slf4j
public class TrackEventConsumer {
private final BaseRedisService baseRedisService;
private final TrackEventService trackEventService;
private static final String REDIS_QUEUE_KEY = "track:event:queue";
private static final String CONSUMER_LOCK_KEY = "track:event:consumer:lock";
private static final int BATCH_SIZE = 100;
@Scheduled(cron = "0/10 * * * * ?") // 每10秒执行一次
public void consumeTrackEvents() {
// 获取分布式锁
String lockId = baseRedisService.lock(CONSUMER_LOCK_KEY, 5000, 1000);
if (lockId == null) {
log.debug("获取消费锁失败,跳过本次消费");
return;
}
try {
// 批量从Redis List取出数据
List<String> eventList = baseRedisService.popBatchFromList(REDIS_QUEUE_KEY);
if (eventList == null || eventList.isEmpty()) {
return;
}
// 转换为实体对象
List<StoreTrackEvent> events = eventList.stream()
.map(json -> JSON.parseObject(json, StoreTrackEvent.class))
.collect(Collectors.toList());
// 批量保存到数据库
trackEventService.batchSaveTrackEvents(events);
log.info("成功消费{}条埋点数据", events.size());
} catch (Exception e) {
log.error("消费埋点数据失败", e);
} finally {
// 释放锁
baseRedisService.unlock(CONSUMER_LOCK_KEY, lockId);
}
}
}
@Override
public Map<String, Object> getBusinessData(Integer storeId, Date startDate, Date endDate, String category) {
Map<String, Object> result = new HashMap<>();
if (category == null || category.equals("TRAFFIC")) {
// 流量数据统计
Map<String, Object> trafficData = new HashMap<>();
// 查询浏览量、访客数等
result.put("trafficData", trafficData);
}
if (category == null || category.equals("INTERACTION")) {
// 互动数据统计
Map<String, Object> interactionData = new HashMap<>();
// 查询收藏、分享等
result.put("interactionData", interactionData);
}
// ... 其他分类数据
return result;
}
CREATE TABLE `store_track_event` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`event_type` varchar(50) NOT NULL COMMENT '事件类型',
`event_category` varchar(50) NOT NULL COMMENT '事件分类',
`user_id` int(11) DEFAULT NULL COMMENT '用户ID',
`store_id` int(11) DEFAULT NULL COMMENT '店铺ID',
`target_id` int(11) DEFAULT NULL COMMENT '目标对象ID',
`target_type` varchar(50) DEFAULT NULL COMMENT '目标对象类型',
`event_data` text COMMENT '事件附加数据(JSON格式)',
`amount` decimal(10,2) DEFAULT NULL COMMENT '金额',
`duration` bigint(20) DEFAULT NULL COMMENT '时长(毫秒)',
`ip_address` varchar(50) DEFAULT NULL COMMENT 'IP地址',
`user_agent` varchar(500) DEFAULT NULL COMMENT '用户代理',
`device_type` varchar(20) DEFAULT NULL COMMENT '设备类型',
`app_version` varchar(20) DEFAULT NULL COMMENT 'APP版本号',
`event_time` datetime NOT NULL COMMENT '事件发生时间',
`created_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`delete_flag` int(1) NOT NULL DEFAULT '0' COMMENT '删除标记',
PRIMARY KEY (`id`),
KEY `idx_store_id` (`store_id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_event_type` (`event_type`),
KEY `idx_event_category` (`event_category`),
KEY `idx_event_time` (`event_time`),
KEY `idx_store_event_time` (`store_id`,`event_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='埋点事件表';
CREATE TABLE `store_track_statistics` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`store_id` int(11) NOT NULL COMMENT '店铺ID',
`stat_date` date NOT NULL COMMENT '统计日期',
`stat_type` varchar(50) NOT NULL COMMENT '统计类型',
`traffic_data` text COMMENT '流量数据(JSON格式)',
`interaction_data` text COMMENT '互动数据(JSON格式)',
`coupon_data` text COMMENT '优惠券数据(JSON格式)',
`voucher_data` text COMMENT '代金券数据(JSON格式)',
`service_data` text COMMENT '服务质量数据(JSON格式)',
`price_ranking_data` text COMMENT '价目表排名数据(JSON格式)',
`created_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_store_date_type` (`store_id`,`stat_date`,`stat_type`),
KEY `idx_store_id` (`store_id`),
KEY `idx_stat_date` (`stat_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='埋点统计表';
// 上报浏览事件
axios.post('/track/event', {
eventType: 'VIEW',
eventCategory: 'TRAFFIC',
storeId: 1001,
targetType: 'STORE',
duration: 3000
});
axios.get('/business/data', {
params: {
storeId: 1001,
startDate: '2026-01-08',
endDate: '2026-01-14'
}
});