|
|
@@ -1,21 +1,17 @@
|
|
|
package shop.alien.store.service.impl;
|
|
|
|
|
|
import com.alibaba.fastjson.JSON;
|
|
|
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
|
import lombok.RequiredArgsConstructor;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
import shop.alien.entity.store.StoreTrackEvent;
|
|
|
-import shop.alien.entity.store.StoreTrackStatistics;
|
|
|
import shop.alien.mapper.StoreTrackEventMapper;
|
|
|
-import shop.alien.mapper.StoreTrackStatisticsMapper;
|
|
|
import shop.alien.store.config.BaseRedisService;
|
|
|
import shop.alien.store.service.TrackEventService;
|
|
|
|
|
|
-import java.util.*;
|
|
|
-import java.util.stream.Collectors;
|
|
|
+import java.util.List;
|
|
|
|
|
|
/**
|
|
|
* 埋点事件服务实现类
|
|
|
@@ -29,8 +25,6 @@ import java.util.stream.Collectors;
|
|
|
@Transactional
|
|
|
public class TrackEventServiceImpl extends ServiceImpl<StoreTrackEventMapper, StoreTrackEvent> implements TrackEventService {
|
|
|
|
|
|
- private final StoreTrackEventMapper trackEventMapper;
|
|
|
- private final StoreTrackStatisticsMapper trackStatisticsMapper;
|
|
|
private final BaseRedisService baseRedisService;
|
|
|
|
|
|
private static final String REDIS_QUEUE_KEY = "track:event:queue";
|
|
|
@@ -64,347 +58,4 @@ public class TrackEventServiceImpl extends ServiceImpl<StoreTrackEventMapper, St
|
|
|
throw new RuntimeException("批量保存埋点事件失败", e);
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- @Override
|
|
|
- public Map<String, Object> getBusinessData(Integer storeId, Date startDate, Date endDate, String category) {
|
|
|
- Map<String, Object> result = new HashMap<>();
|
|
|
-
|
|
|
- if (category == null || "TRAFFIC".equals(category)) {
|
|
|
- Map<String, Object> trafficData = getTrafficData(storeId, startDate, endDate);
|
|
|
- result.put("trafficData", trafficData);
|
|
|
- }
|
|
|
-
|
|
|
- if (category == null || "INTERACTION".equals(category)) {
|
|
|
- Map<String, Object> interactionData = getInteractionData(storeId, startDate, endDate);
|
|
|
- result.put("interactionData", interactionData);
|
|
|
- }
|
|
|
-
|
|
|
- if (category == null || "COUPON".equals(category)) {
|
|
|
- Map<String, Object> couponData = getCouponData(storeId, startDate, endDate);
|
|
|
- result.put("couponData", couponData);
|
|
|
- }
|
|
|
-
|
|
|
- if (category == null || "VOUCHER".equals(category)) {
|
|
|
- Map<String, Object> voucherData = getVoucherData(storeId, startDate, endDate);
|
|
|
- result.put("voucherData", voucherData);
|
|
|
- }
|
|
|
-
|
|
|
- if (category == null || "SERVICE".equals(category)) {
|
|
|
- Map<String, Object> serviceData = getServiceData(storeId, startDate, endDate);
|
|
|
- result.put("serviceData", serviceData);
|
|
|
- }
|
|
|
-
|
|
|
- if (category == null || "PRICE".equals(category)) {
|
|
|
- List<Map<String, Object>> priceRankingData = getPriceRankingData(storeId, startDate, endDate, 10);
|
|
|
- result.put("priceRankingData", priceRankingData);
|
|
|
- }
|
|
|
-
|
|
|
- return result;
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public Map<String, Object> compareBusinessData(Integer storeId, Date startDate1, Date endDate1, Date startDate2, Date endDate2) {
|
|
|
- Map<String, Object> result = new HashMap<>();
|
|
|
-
|
|
|
- // 获取时间段1的数据
|
|
|
- Map<String, Object> period1Data = getBusinessData(storeId, startDate1, endDate1, null);
|
|
|
- result.put("period1", period1Data);
|
|
|
-
|
|
|
- // 获取时间段2的数据
|
|
|
- Map<String, Object> period2Data = getBusinessData(storeId, startDate2, endDate2, null);
|
|
|
- result.put("period2", period2Data);
|
|
|
-
|
|
|
- // 计算对比数据
|
|
|
- Map<String, Object> compareData = calculateCompareData(period1Data, period2Data);
|
|
|
- result.put("compare", compareData);
|
|
|
-
|
|
|
- return result;
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void calculateAndSaveStatistics(Integer storeId, Date statDate, String statType) {
|
|
|
- // 计算统计数据
|
|
|
- Map<String, Object> businessData = getBusinessData(storeId, statDate, statDate, null);
|
|
|
-
|
|
|
- // 查询或创建统计记录
|
|
|
- LambdaQueryWrapper<StoreTrackStatistics> queryWrapper = new LambdaQueryWrapper<>();
|
|
|
- queryWrapper.eq(StoreTrackStatistics::getStoreId, storeId)
|
|
|
- .eq(StoreTrackStatistics::getStatDate, statDate)
|
|
|
- .eq(StoreTrackStatistics::getStatType, statType);
|
|
|
-
|
|
|
- StoreTrackStatistics statistics = trackStatisticsMapper.selectOne(queryWrapper);
|
|
|
- if (statistics == null) {
|
|
|
- statistics = new StoreTrackStatistics();
|
|
|
- statistics.setStoreId(storeId);
|
|
|
- statistics.setStatDate(statDate);
|
|
|
- statistics.setStatType(statType);
|
|
|
- }
|
|
|
-
|
|
|
- // 设置统计数据
|
|
|
- Map<String, Object> trafficData = (Map<String, Object>) businessData.get("trafficData");
|
|
|
- if (trafficData != null) {
|
|
|
- statistics.setTrafficData(JSON.toJSONString(trafficData));
|
|
|
- }
|
|
|
-
|
|
|
- Map<String, Object> interactionData = (Map<String, Object>) businessData.get("interactionData");
|
|
|
- if (interactionData != null) {
|
|
|
- statistics.setInteractionData(JSON.toJSONString(interactionData));
|
|
|
- }
|
|
|
-
|
|
|
- Map<String, Object> couponData = (Map<String, Object>) businessData.get("couponData");
|
|
|
- if (couponData != null) {
|
|
|
- statistics.setCouponData(JSON.toJSONString(couponData));
|
|
|
- }
|
|
|
-
|
|
|
- Map<String, Object> voucherData = (Map<String, Object>) businessData.get("voucherData");
|
|
|
- if (voucherData != null) {
|
|
|
- statistics.setVoucherData(JSON.toJSONString(voucherData));
|
|
|
- }
|
|
|
-
|
|
|
- Map<String, Object> serviceData = (Map<String, Object>) businessData.get("serviceData");
|
|
|
- if (serviceData != null) {
|
|
|
- statistics.setServiceData(JSON.toJSONString(serviceData));
|
|
|
- }
|
|
|
-
|
|
|
- List<Map<String, Object>> priceRankingData = (List<Map<String, Object>>) businessData.get("priceRankingData");
|
|
|
- if (priceRankingData != null) {
|
|
|
- statistics.setPriceRankingData(JSON.toJSONString(priceRankingData));
|
|
|
- }
|
|
|
-
|
|
|
- // 保存统计记录
|
|
|
- if (statistics.getId() == null) {
|
|
|
- trackStatisticsMapper.insert(statistics);
|
|
|
- } else {
|
|
|
- trackStatisticsMapper.updateById(statistics);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public List<Map<String, Object>> getPriceRankingData(Integer storeId, Date startDate, Date endDate, Integer limit) {
|
|
|
- // 查询价目表相关的埋点数据
|
|
|
- LambdaQueryWrapper<StoreTrackEvent> queryWrapper = new LambdaQueryWrapper<>();
|
|
|
- queryWrapper.eq(StoreTrackEvent::getStoreId, storeId)
|
|
|
- .in(StoreTrackEvent::getEventType, Arrays.asList("PRICE_VIEW", "PRICE_SHARE"))
|
|
|
- .eq(StoreTrackEvent::getTargetType, "PRICE")
|
|
|
- .ge(StoreTrackEvent::getEventTime, startDate)
|
|
|
- .le(StoreTrackEvent::getEventTime, endDate)
|
|
|
- .eq(StoreTrackEvent::getDeleteFlag, 0);
|
|
|
-
|
|
|
- List<StoreTrackEvent> events = trackEventMapper.selectList(queryWrapper);
|
|
|
-
|
|
|
- // 按targetId聚合统计
|
|
|
- Map<Integer, Map<String, Object>> priceMap = new HashMap<>();
|
|
|
- for (StoreTrackEvent event : events) {
|
|
|
- Integer targetId = event.getTargetId();
|
|
|
- if (targetId == null) {
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- Map<String, Object> priceStat = priceMap.computeIfAbsent(targetId, k -> {
|
|
|
- Map<String, Object> stat = new HashMap<>();
|
|
|
- stat.put("priceId", targetId);
|
|
|
- stat.put("viewCount", 0);
|
|
|
- stat.put("visitorCount", 0);
|
|
|
- stat.put("shareCount", 0);
|
|
|
- stat.put("visitors", new HashSet<>());
|
|
|
- return stat;
|
|
|
- });
|
|
|
-
|
|
|
- if ("PRICE_VIEW".equals(event.getEventType())) {
|
|
|
- Integer viewCount = (Integer) priceStat.get("viewCount");
|
|
|
- priceStat.put("viewCount", viewCount + 1);
|
|
|
-
|
|
|
- // 统计访客数(去重)
|
|
|
- Set<Integer> visitors = (Set<Integer>) priceStat.get("visitors");
|
|
|
- if (event.getUserId() != null) {
|
|
|
- visitors.add(event.getUserId());
|
|
|
- }
|
|
|
- priceStat.put("visitorCount", visitors.size());
|
|
|
- } else if ("PRICE_SHARE".equals(event.getEventType())) {
|
|
|
- Integer shareCount = (Integer) priceStat.get("shareCount");
|
|
|
- priceStat.put("shareCount", shareCount + 1);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 转换为列表并按浏览量排序
|
|
|
- List<Map<String, Object>> result = priceMap.values().stream()
|
|
|
- .sorted((a, b) -> Integer.compare((Integer) b.get("viewCount"), (Integer) a.get("viewCount")))
|
|
|
- .limit(limit != null ? limit : 10)
|
|
|
- .map(stat -> {
|
|
|
- Map<String, Object> item = new HashMap<>();
|
|
|
- item.put("priceId", stat.get("priceId"));
|
|
|
- item.put("viewCount", stat.get("viewCount"));
|
|
|
- item.put("visitorCount", stat.get("visitorCount"));
|
|
|
- item.put("shareCount", stat.get("shareCount"));
|
|
|
- return item;
|
|
|
- })
|
|
|
- .collect(Collectors.toList());
|
|
|
-
|
|
|
- return result;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 获取流量数据
|
|
|
- */
|
|
|
- private Map<String, Object> getTrafficData(Integer storeId, Date startDate, Date endDate) {
|
|
|
- Map<String, Object> result = new HashMap<>();
|
|
|
-
|
|
|
- LambdaQueryWrapper<StoreTrackEvent> queryWrapper = new LambdaQueryWrapper<>();
|
|
|
- queryWrapper.eq(StoreTrackEvent::getStoreId, storeId)
|
|
|
- .eq(StoreTrackEvent::getEventCategory, "TRAFFIC")
|
|
|
- .ge(StoreTrackEvent::getEventTime, startDate)
|
|
|
- .le(StoreTrackEvent::getEventTime, endDate)
|
|
|
- .eq(StoreTrackEvent::getDeleteFlag, 0);
|
|
|
-
|
|
|
- List<StoreTrackEvent> events = trackEventMapper.selectList(queryWrapper);
|
|
|
-
|
|
|
- // 统计搜索量
|
|
|
- long searchCount = events.stream()
|
|
|
- .filter(e -> "SEARCH".equals(e.getEventType()))
|
|
|
- .count();
|
|
|
- result.put("searchCount", searchCount);
|
|
|
-
|
|
|
- // 统计浏览量
|
|
|
- long viewCount = events.stream()
|
|
|
- .filter(e -> "VIEW".equals(e.getEventType()))
|
|
|
- .count();
|
|
|
- result.put("viewCount", viewCount);
|
|
|
-
|
|
|
- // 统计访客数(去重)
|
|
|
- Set<Integer> visitors = events.stream()
|
|
|
- .filter(e -> "VIEW".equals(e.getEventType()) && e.getUserId() != null)
|
|
|
- .map(StoreTrackEvent::getUserId)
|
|
|
- .collect(Collectors.toSet());
|
|
|
- result.put("visitorCount", visitors.size());
|
|
|
-
|
|
|
- // 统计新增访客数(需要查询历史数据判断)
|
|
|
- // TODO: 实现新增访客数统计逻辑
|
|
|
-
|
|
|
- // 统计访问时长
|
|
|
- long totalDuration = events.stream()
|
|
|
- .filter(e -> "VIEW".equals(e.getEventType()) && e.getDuration() != null)
|
|
|
- .mapToLong(StoreTrackEvent::getDuration)
|
|
|
- .sum();
|
|
|
- result.put("totalDuration", totalDuration);
|
|
|
-
|
|
|
- // 计算平均访问时长
|
|
|
- long viewCountWithDuration = events.stream()
|
|
|
- .filter(e -> "VIEW".equals(e.getEventType()) && e.getDuration() != null)
|
|
|
- .count();
|
|
|
- long avgDuration = viewCountWithDuration > 0 ? totalDuration / viewCountWithDuration : 0;
|
|
|
- result.put("avgDuration", avgDuration);
|
|
|
-
|
|
|
- return result;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 获取互动数据
|
|
|
- */
|
|
|
- private Map<String, Object> getInteractionData(Integer storeId, Date startDate, Date endDate) {
|
|
|
- Map<String, Object> result = new HashMap<>();
|
|
|
-
|
|
|
- LambdaQueryWrapper<StoreTrackEvent> queryWrapper = new LambdaQueryWrapper<>();
|
|
|
- queryWrapper.eq(StoreTrackEvent::getStoreId, storeId)
|
|
|
- .eq(StoreTrackEvent::getEventCategory, "INTERACTION")
|
|
|
- .ge(StoreTrackEvent::getEventTime, startDate)
|
|
|
- .le(StoreTrackEvent::getEventTime, endDate)
|
|
|
- .eq(StoreTrackEvent::getDeleteFlag, 0);
|
|
|
-
|
|
|
- List<StoreTrackEvent> events = trackEventMapper.selectList(queryWrapper);
|
|
|
-
|
|
|
- // 统计各类互动数据
|
|
|
- result.put("collectCount", events.stream().filter(e -> "COLLECT".equals(e.getEventType())).count());
|
|
|
- result.put("shareCount", events.stream().filter(e -> "SHARE".equals(e.getEventType())).count());
|
|
|
- result.put("checkinCount", events.stream().filter(e -> "CHECKIN".equals(e.getEventType())).count());
|
|
|
- result.put("consultCount", events.stream().filter(e -> "CONSULT".equals(e.getEventType())).count());
|
|
|
-
|
|
|
- // TODO: 实现好友、关注、粉丝、动态等数据的统计(需要从其他表查询)
|
|
|
-
|
|
|
- return result;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 获取优惠券数据
|
|
|
- */
|
|
|
- private Map<String, Object> getCouponData(Integer storeId, Date startDate, Date endDate) {
|
|
|
- Map<String, Object> result = new HashMap<>();
|
|
|
-
|
|
|
- // TODO: 实现优惠券数据统计(需要从优惠券相关表查询)
|
|
|
-
|
|
|
- return result;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 获取代金券数据
|
|
|
- */
|
|
|
- private Map<String, Object> getVoucherData(Integer storeId, Date startDate, Date endDate) {
|
|
|
- Map<String, Object> result = new HashMap<>();
|
|
|
-
|
|
|
- // TODO: 实现代金券数据统计(需要从代金券相关表查询)
|
|
|
-
|
|
|
- return result;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 获取服务质量数据
|
|
|
- */
|
|
|
- private Map<String, Object> getServiceData(Integer storeId, Date startDate, Date endDate) {
|
|
|
- Map<String, Object> result = new HashMap<>();
|
|
|
-
|
|
|
- // TODO: 实现服务质量数据统计(需要从评价表查询)
|
|
|
-
|
|
|
- return result;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 计算对比数据
|
|
|
- */
|
|
|
- private Map<String, Object> calculateCompareData(Map<String, Object> period1Data, Map<String, Object> period2Data) {
|
|
|
- Map<String, Object> compareData = new HashMap<>();
|
|
|
-
|
|
|
- // 对比流量数据
|
|
|
- Map<String, Object> traffic1 = (Map<String, Object>) period1Data.get("trafficData");
|
|
|
- Map<String, Object> traffic2 = (Map<String, Object>) period2Data.get("trafficData");
|
|
|
-
|
|
|
- if (traffic1 != null && traffic2 != null) {
|
|
|
- Map<String, Object> trafficCompare = new HashMap<>();
|
|
|
-
|
|
|
- Long viewCount1 = getLongValue(traffic1, "viewCount");
|
|
|
- Long viewCount2 = getLongValue(traffic2, "viewCount");
|
|
|
- if (viewCount1 != null && viewCount2 != null) {
|
|
|
- double changePercent = calculateChangePercent(viewCount1, viewCount2);
|
|
|
- trafficCompare.put("viewCountChange", changePercent);
|
|
|
- }
|
|
|
-
|
|
|
- compareData.put("traffic", trafficCompare);
|
|
|
- }
|
|
|
-
|
|
|
- // TODO: 实现其他数据的对比
|
|
|
-
|
|
|
- return compareData;
|
|
|
- }
|
|
|
-
|
|
|
- private Long getLongValue(Map<String, Object> map, String key) {
|
|
|
- Object value = map.get(key);
|
|
|
- if (value == null) {
|
|
|
- return null;
|
|
|
- }
|
|
|
- if (value instanceof Long) {
|
|
|
- return (Long) value;
|
|
|
- }
|
|
|
- if (value instanceof Integer) {
|
|
|
- return ((Integer) value).longValue();
|
|
|
- }
|
|
|
- if (value instanceof Number) {
|
|
|
- return ((Number) value).longValue();
|
|
|
- }
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- private double calculateChangePercent(long value1, long value2) {
|
|
|
- if (value2 == 0) {
|
|
|
- return value1 > 0 ? 100.0 : 0.0;
|
|
|
- }
|
|
|
- return ((double) (value1 - value2) / value2) * 100.0;
|
|
|
- }
|
|
|
-}
|
|
|
+}
|