埋点实现代码清单.md 10 KB

埋点实现代码清单

一、已创建的文件

1. 实体类(Entity)

  • alien-entity/src/main/java/shop/alien/entity/store/StoreTrackEvent.java - 埋点事件实体类
  • alien-entity/src/main/java/shop/alien/entity/store/StoreTrackStatistics.java - 埋点统计数据实体类

2. 注解(Annotation)

  • alien-store/src/main/java/shop/alien/store/annotation/TrackEvent.java - 埋点注解

3. 服务接口(Service Interface)

  • alien-store/src/main/java/shop/alien/store/service/TrackEventService.java - 埋点事件服务接口

4. Controller

  • alien-store/src/main/java/shop/alien/store/controller/TrackEventController.java - 埋点上报接口

5. 文档

  • alien-store/doc/埋点需求完整方案.md - 完整方案文档

二、待创建的文件(核心代码)

1. AOP切面(重要)

文件路径: alien-store/src/main/java/shop/alien/store/aspect/TrackEventAspect.java

功能: 拦截标注了@TrackEvent注解的方法,自动收集埋点数据并写入Redis List

关键代码要点:

  • 使用@Around环绕通知
  • 解析SpEL表达式获取storeIduserId等参数
  • 调用BaseRedisService.setListRight()写入Redis List
  • 使用@Order注解设置切面执行顺序

2. Service实现类(重要)

文件路径: alien-store/src/main/java/shop/alien/store/service/impl/TrackEventServiceImpl.java

功能:

  • 实现saveTrackEvent()方法,将埋点数据写入Redis List
  • 实现batchSaveTrackEvents()方法,批量保存到数据库
  • 实现getBusinessData()方法,统计查询经营数据
  • 实现compareBusinessData()方法,对比数据
  • 实现calculateAndSaveStatistics()方法,计算统计数据
  • 实现getPriceRankingData()方法,获取价目表排名

3. 数据消费服务(重要)

文件路径: alien-store/src/main/java/shop/alien/store/service/TrackEventConsumer.java

功能: 定时任务,从Redis List批量消费数据并写入数据库

关键代码要点:

  • 使用@Scheduled(cron = "0/10 * * * * ?")每10秒执行一次
  • 使用分布式锁防止多实例重复消费
  • 每次从Redis List取出100条数据
  • 批量保存到数据库

4. Mapper接口

文件路径: alien-store/src/main/java/shop/alien/mapper/StoreTrackEventMapper.java 文件路径: alien-store/src/main/java/shop/alien/mapper/StoreTrackStatisticsMapper.java

5. 统计查询Controller

文件路径: alien-store/src/main/java/shop/alien/store/controller/BusinessDataController.java

接口列表:

  • GET /business/data - 查询经营数据
  • GET /business/data/compare - 数据对比
  • GET /business/data/history - 历史数据查询

6. AI推荐Controller

文件路径: alien-store/src/main/java/shop/alien/store/controller/AIRecoveryController.java

接口列表:

  • GET /business/ai/recommendation - 获取AI推荐

三、关键代码示例

3.1 AOP切面核心代码

@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等
        // ...
    }
}

3.2 Redis List消费核心代码

@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);
        }
    }
}

3.3 统计数据查询核心代码

@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;
}

四、数据库表SQL

4.1 埋点事件表

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='埋点事件表';

4.2 埋点统计表

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='埋点统计表';

五、前端调用示例

5.1 上报埋点事件

// 上报浏览事件
axios.post('/track/event', {
  eventType: 'VIEW',
  eventCategory: 'TRAFFIC',
  storeId: 1001,
  targetType: 'STORE',
  duration: 3000
});

5.2 查询经营数据

axios.get('/business/data', {
  params: {
    storeId: 1001,
    startDate: '2026-01-08',
    endDate: '2026-01-14'
  }
});

六、后续开发步骤

  1. ✅ 创建实体类和注解(已完成)
  2. ⬜ 实现AOP切面(TrackEventAspect)
  3. ⬜ 实现Service实现类(TrackEventServiceImpl)
  4. ⬜ 实现数据消费服务(TrackEventConsumer)
  5. ⬜ 创建Mapper接口
  6. ⬜ 实现统计查询Controller(BusinessDataController)
  7. ⬜ 实现AI推荐Controller(AIRecoveryController)
  8. ⬜ 执行数据库建表SQL
  9. ⬜ 编写单元测试
  10. ⬜ 联调测试

七、注意事项

  1. Redis List需要设置最大长度,防止内存溢出
  2. 消费服务需要异常处理和重试机制
  3. 统计数据建议使用定时任务预计算
  4. AI推荐接口需要缓存,避免频繁调用AI服务
  5. 埋点数据量大,需要定期清理历史数据