|
|
@@ -48,55 +48,116 @@ public class StoreOperationalStatisticsServiceImpl implements StoreOperationalSt
|
|
|
private final StorePriceMapper storePriceMapper;
|
|
|
private final StoreUserMapper storeUserMapper;
|
|
|
private final StoreOperationalStatisticsHistoryMapper statisticsHistoryMapper;
|
|
|
+ private final StoreTrackStatisticsMapper storeTrackStatisticsMapper;
|
|
|
|
|
|
private static final String DATE_FORMAT = "yyyy-MM-dd";
|
|
|
+ private static final String STAT_TYPE_DAILY = "DAILY";
|
|
|
|
|
|
@Override
|
|
|
public StoreOperationalStatisticsVo getStatistics(Integer storeId, String startTime, String endTime) {
|
|
|
log.info("StoreOperationalStatisticsServiceImpl.getStatistics - storeId={}, startTime={}, endTime={}", storeId, startTime, endTime);
|
|
|
+ return calculateStatistics(storeId, startTime, endTime);
|
|
|
+ }
|
|
|
|
|
|
- StoreOperationalStatisticsVo statistics = new StoreOperationalStatisticsVo();
|
|
|
-
|
|
|
+ /**
|
|
|
+ * 计算统计数据(从store_track_statistics表查询并累加)
|
|
|
+ */
|
|
|
+ private StoreOperationalStatisticsVo calculateStatistics(Integer storeId, String startTime, String endTime) {
|
|
|
try {
|
|
|
// 解析时间范围
|
|
|
SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT);
|
|
|
Date startDate = sdf.parse(startTime);
|
|
|
Date endDate = sdf.parse(endTime);
|
|
|
- // 设置为当天的结束时间
|
|
|
- Calendar endCal = Calendar.getInstance();
|
|
|
- endCal.setTime(endDate);
|
|
|
- endCal.set(Calendar.HOUR_OF_DAY, 23);
|
|
|
- endCal.set(Calendar.MINUTE, 59);
|
|
|
- endCal.set(Calendar.SECOND, 59);
|
|
|
- endDate = endCal.getTime();
|
|
|
-
|
|
|
- // 1. 流量数据统计
|
|
|
- statistics.setTrafficData(getTrafficData(storeId, startDate, endDate));
|
|
|
-
|
|
|
- // 2. 互动数据统计
|
|
|
- statistics.setInteractionData(getInteractionData(storeId, startDate, endDate));
|
|
|
-
|
|
|
- // 3. 优惠券数据统计
|
|
|
- statistics.setCouponData(getCouponData(storeId, startDate, endDate));
|
|
|
-
|
|
|
- // 4. 代金券数据统计(与优惠券类似,根据实际业务区分)
|
|
|
- statistics.setVoucherData(getVoucherData(storeId, startDate, endDate));
|
|
|
-
|
|
|
- // 5. 服务质量数据统计
|
|
|
- statistics.setServiceQualityData(getServiceQualityData(storeId, startDate, endDate));
|
|
|
-
|
|
|
- // 6. 价目表排名统计
|
|
|
- statistics.setPriceListRanking(getPriceListRanking(storeId, startDate, endDate));
|
|
|
-
|
|
|
- // 保存统计数据到历史表
|
|
|
- saveStatisticsHistory(storeId, startTime, endTime, statistics);
|
|
|
-
|
|
|
+
|
|
|
+ // 查询指定时间范围内的日统计数据
|
|
|
+ LambdaQueryWrapper<StoreTrackStatistics> wrapper = new LambdaQueryWrapper<>();
|
|
|
+ wrapper.eq(StoreTrackStatistics::getStoreId, storeId)
|
|
|
+ .eq(StoreTrackStatistics::getStatType, STAT_TYPE_DAILY)
|
|
|
+ .ge(StoreTrackStatistics::getStatDate, startDate)
|
|
|
+ .le(StoreTrackStatistics::getStatDate, endDate);
|
|
|
+
|
|
|
+ List<StoreTrackStatistics> statisticsList = storeTrackStatisticsMapper.selectList(wrapper);
|
|
|
+
|
|
|
+ if (statisticsList == null || statisticsList.isEmpty()) {
|
|
|
+ log.warn("未查询到统计数据 - storeId={}, startTime={}, endTime={}", storeId, startTime, endTime);
|
|
|
+ return new StoreOperationalStatisticsVo();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 累加所有统计数据
|
|
|
+ return aggregateStatistics(statisticsList);
|
|
|
+
|
|
|
} catch (ParseException e) {
|
|
|
- log.error("StoreOperationalStatisticsServiceImpl.getStatistics - 时间解析错误", e);
|
|
|
+ log.error("StoreOperationalStatisticsServiceImpl.calculateStatistics - 时间解析错误", e);
|
|
|
throw new RuntimeException("时间格式错误: " + e.getMessage());
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("计算统计数据失败 - storeId={}, startTime={}, endTime={}, error={}",
|
|
|
+ storeId, startTime, endTime, e.getMessage(), e);
|
|
|
+ throw new RuntimeException("计算统计数据失败: " + e.getMessage());
|
|
|
}
|
|
|
-
|
|
|
- return statistics;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 累加统计数据
|
|
|
+ */
|
|
|
+ private StoreOperationalStatisticsVo aggregateStatistics(List<StoreTrackStatistics> statisticsList) {
|
|
|
+ StoreOperationalStatisticsVo result = new StoreOperationalStatisticsVo();
|
|
|
+
|
|
|
+ // 初始化累加器
|
|
|
+ Map<String, Long> trafficAccumulator = new HashMap<>();
|
|
|
+ Map<String, Long> interactionAccumulator = new HashMap<>();
|
|
|
+ Map<String, Object> couponAccumulator = new HashMap<>();
|
|
|
+ Map<String, Object> voucherAccumulator = new HashMap<>();
|
|
|
+ Map<String, Object> serviceAccumulator = new HashMap<>();
|
|
|
+ Map<Integer, Map<String, Long>> priceRankingAccumulator = new HashMap<>();
|
|
|
+
|
|
|
+ // 用于计算平均值的计数器
|
|
|
+ int trafficCount = 0;
|
|
|
+ int serviceCount = 0;
|
|
|
+
|
|
|
+ // 遍历所有统计数据并累加
|
|
|
+ for (StoreTrackStatistics stat : statisticsList) {
|
|
|
+ // 累加流量数据
|
|
|
+ if (stat.getTrafficData() != null && !stat.getTrafficData().isEmpty()) {
|
|
|
+ aggregateTrafficData(stat.getTrafficData(), trafficAccumulator);
|
|
|
+ trafficCount++;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 累加互动数据
|
|
|
+ if (stat.getInteractionData() != null && !stat.getInteractionData().isEmpty()) {
|
|
|
+ aggregateInteractionData(stat.getInteractionData(), interactionAccumulator);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 累加优惠券数据
|
|
|
+ if (stat.getCouponData() != null && !stat.getCouponData().isEmpty()) {
|
|
|
+ aggregateCouponData(stat.getCouponData(), couponAccumulator);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 累加代金券数据
|
|
|
+ if (stat.getVoucherData() != null && !stat.getVoucherData().isEmpty()) {
|
|
|
+ aggregateVoucherData(stat.getVoucherData(), voucherAccumulator);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 累加服务质量数据
|
|
|
+ if (stat.getServiceData() != null && !stat.getServiceData().isEmpty()) {
|
|
|
+ aggregateServiceData(stat.getServiceData(), serviceAccumulator);
|
|
|
+ serviceCount++;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 累加价目表排名数据
|
|
|
+ if (stat.getPriceRankingData() != null && !stat.getPriceRankingData().isEmpty()) {
|
|
|
+ aggregatePriceRankingData(stat.getPriceRankingData(), priceRankingAccumulator);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 转换为VO对象
|
|
|
+ result.setTrafficData(convertToTrafficDataVo(trafficAccumulator, trafficCount));
|
|
|
+ result.setInteractionData(convertToInteractionDataVo(interactionAccumulator));
|
|
|
+ result.setCouponData(convertToCouponDataVo(couponAccumulator));
|
|
|
+ result.setVoucherData(convertToVoucherDataVo(voucherAccumulator));
|
|
|
+ result.setServiceQualityData(convertToServiceQualityDataVo(serviceAccumulator, serviceCount));
|
|
|
+ result.setPriceListRanking(convertToPriceListRankingVo(priceRankingAccumulator));
|
|
|
+
|
|
|
+ return result;
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
@@ -111,9 +172,9 @@ public class StoreOperationalStatisticsServiceImpl implements StoreOperationalSt
|
|
|
comparison.setPreviousStartTime(previousStartTime);
|
|
|
comparison.setPreviousEndTime(previousEndTime);
|
|
|
|
|
|
- // 获取当期和上期的统计数据(getStatistics方法内部已经保存了历史记录)
|
|
|
- StoreOperationalStatisticsVo currentStatistics = getStatistics(storeId, currentStartTime, currentEndTime);
|
|
|
- StoreOperationalStatisticsVo previousStatistics = getStatistics(storeId, previousStartTime, previousEndTime);
|
|
|
+ // 获取当期和上期的统计数据(不保存历史,对比接口不需要保存)
|
|
|
+ StoreOperationalStatisticsVo currentStatistics = calculateStatistics(storeId, currentStartTime, currentEndTime);
|
|
|
+ StoreOperationalStatisticsVo previousStatistics = calculateStatistics(storeId, previousStartTime, previousEndTime);
|
|
|
|
|
|
// 构建对比数据
|
|
|
comparison.setTrafficData(buildTrafficDataComparison(currentStatistics.getTrafficData(), previousStatistics.getTrafficData()));
|
|
|
@@ -125,32 +186,142 @@ public class StoreOperationalStatisticsServiceImpl implements StoreOperationalSt
|
|
|
return comparison;
|
|
|
}
|
|
|
|
|
|
+
|
|
|
/**
|
|
|
- * 保存统计数据到历史表
|
|
|
+ * 将StoreOperationalStatisticsVo转换为符合埋点统计数据JSON格式
|
|
|
*/
|
|
|
- private void saveStatisticsHistory(Integer storeId, String startTime, String endTime, StoreOperationalStatisticsVo statistics) {
|
|
|
- try {
|
|
|
- SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT);
|
|
|
- Date startDate = sdf.parse(startTime);
|
|
|
- Date endDate = sdf.parse(endTime);
|
|
|
-
|
|
|
- StoreOperationalStatisticsHistory history = new StoreOperationalStatisticsHistory();
|
|
|
- history.setStoreId(storeId);
|
|
|
- history.setStartTime(startDate);
|
|
|
- history.setEndTime(endDate);
|
|
|
- history.setQueryTime(new Date());
|
|
|
-
|
|
|
- // 将统计数据序列化为JSON字符串
|
|
|
- String statisticsJson = JSON.toJSONString(statistics);
|
|
|
- history.setStatisticsData(statisticsJson);
|
|
|
-
|
|
|
- statisticsHistoryMapper.insert(history);
|
|
|
- log.info("保存统计数据到历史表成功 - storeId={}, startTime={}, endTime={}", storeId, startTime, endTime);
|
|
|
- } catch (Exception e) {
|
|
|
- log.error("保存统计数据到历史表失败 - storeId={}, startTime={}, endTime={}, error={}",
|
|
|
- storeId, startTime, endTime, e.getMessage(), e);
|
|
|
- // 保存失败不影响主流程,只记录日志
|
|
|
+ private Map<String, Object> convertToTrackStatisticsFormat(StoreOperationalStatisticsVo statistics) {
|
|
|
+ Map<String, Object> result = new HashMap<>();
|
|
|
+
|
|
|
+ // 1. 转换流量数据
|
|
|
+ if (statistics.getTrafficData() != null) {
|
|
|
+ StoreOperationalStatisticsVo.TrafficData traffic = statistics.getTrafficData();
|
|
|
+ Map<String, Object> trafficData = new HashMap<>();
|
|
|
+ trafficData.put("searchCount", traffic.getStoreSearchVolume() != null ? traffic.getStoreSearchVolume() : 0L);
|
|
|
+ trafficData.put("viewCount", traffic.getPageViews() != null ? traffic.getPageViews() : 0L);
|
|
|
+ trafficData.put("visitorCount", traffic.getVisitors() != null ? traffic.getVisitors() : 0L);
|
|
|
+ trafficData.put("newVisitorCount", traffic.getNewVisitors() != null ? traffic.getNewVisitors() : 0L);
|
|
|
+ // 访问时长从秒转换为毫秒
|
|
|
+ trafficData.put("totalDuration", traffic.getVisitDuration() != null ? traffic.getVisitDuration() * 1000 : 0L);
|
|
|
+ trafficData.put("avgDuration", traffic.getAvgVisitDuration() != null ? traffic.getAvgVisitDuration() * 1000 : 0L);
|
|
|
+ result.put("trafficData", trafficData);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 转换互动数据
|
|
|
+ if (statistics.getInteractionData() != null) {
|
|
|
+ StoreOperationalStatisticsVo.InteractionData interaction = statistics.getInteractionData();
|
|
|
+ Map<String, Object> interactionData = new HashMap<>();
|
|
|
+ interactionData.put("collectCount", interaction.getStoreCollectionCount() != null ? interaction.getStoreCollectionCount() : 0L);
|
|
|
+ interactionData.put("shareCount", interaction.getStoreShareCount() != null ? interaction.getStoreShareCount() : 0L);
|
|
|
+ interactionData.put("checkinCount", interaction.getStoreCheckInCount() != null ? interaction.getStoreCheckInCount() : 0L);
|
|
|
+ interactionData.put("consultCount", interaction.getConsultMerchantCount() != null ? interaction.getConsultMerchantCount() : 0L);
|
|
|
+ interactionData.put("friendCount", interaction.getFriendsCount() != null ? interaction.getFriendsCount() : 0L);
|
|
|
+ interactionData.put("followCount", interaction.getFollowCount() != null ? interaction.getFollowCount() : 0L);
|
|
|
+ interactionData.put("fansCount", interaction.getFansCount() != null ? interaction.getFansCount() : 0L);
|
|
|
+ interactionData.put("postCount", interaction.getPostsPublishedCount() != null ? interaction.getPostsPublishedCount() : 0L);
|
|
|
+ interactionData.put("postLikeCount", interaction.getPostLikesCount() != null ? interaction.getPostLikesCount() : 0L);
|
|
|
+ interactionData.put("postCommentCount", interaction.getPostCommentsCount() != null ? interaction.getPostCommentsCount() : 0L);
|
|
|
+ interactionData.put("postRepostCount", interaction.getPostSharesCount() != null ? interaction.getPostSharesCount() : 0L);
|
|
|
+ interactionData.put("reportCount", interaction.getReportedCount() != null ? interaction.getReportedCount() : 0L);
|
|
|
+ interactionData.put("blockCount", interaction.getBlockedCount() != null ? interaction.getBlockedCount() : 0L);
|
|
|
+ result.put("interactionData", interactionData);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 转换优惠券数据
|
|
|
+ if (statistics.getCouponData() != null) {
|
|
|
+ StoreOperationalStatisticsVo.CouponData coupon = statistics.getCouponData();
|
|
|
+ Map<String, Object> couponData = new HashMap<>();
|
|
|
+ couponData.put("giveToFriendCount", coupon.getGiftToFriendsCount() != null ? coupon.getGiftToFriendsCount() : 0L);
|
|
|
+ couponData.put("giveToFriendAmount", coupon.getGiftToFriendsAmount() != null ? coupon.getGiftToFriendsAmount() : BigDecimal.ZERO);
|
|
|
+ couponData.put("giveToFriendUseCount", coupon.getGiftToFriendsUsedCount() != null ? coupon.getGiftToFriendsUsedCount() : 0L);
|
|
|
+ couponData.put("giveToFriendUseAmount", coupon.getGiftToFriendsUsedAmount() != null ? coupon.getGiftToFriendsUsedAmount() : BigDecimal.ZERO);
|
|
|
+ // 百分比转换:BigDecimal -> Double
|
|
|
+ if (coupon.getGiftToFriendsUsedAmountRatio() != null) {
|
|
|
+ couponData.put("giveToFriendUseAmountPercent", coupon.getGiftToFriendsUsedAmountRatio().doubleValue());
|
|
|
+ } else {
|
|
|
+ couponData.put("giveToFriendUseAmountPercent", 0.0);
|
|
|
+ }
|
|
|
+ couponData.put("friendGiveCount", coupon.getFriendsGiftCount() != null ? coupon.getFriendsGiftCount() : 0L);
|
|
|
+ couponData.put("friendGiveAmount", coupon.getFriendsGiftAmount() != null ? coupon.getFriendsGiftAmount() : BigDecimal.ZERO);
|
|
|
+ couponData.put("friendGiveUseCount", coupon.getFriendsGiftUsedCount() != null ? coupon.getFriendsGiftUsedCount() : 0L);
|
|
|
+ couponData.put("friendGiveUseAmount", coupon.getFriendsGiftUsedAmount() != null ? coupon.getFriendsGiftUsedAmount() : BigDecimal.ZERO);
|
|
|
+ if (coupon.getFriendsGiftUsedAmountRatio() != null) {
|
|
|
+ couponData.put("friendGiveUseAmountPercent", coupon.getFriendsGiftUsedAmountRatio().doubleValue());
|
|
|
+ } else {
|
|
|
+ couponData.put("friendGiveUseAmountPercent", 0.0);
|
|
|
+ }
|
|
|
+ result.put("couponData", couponData);
|
|
|
}
|
|
|
+
|
|
|
+ // 4. 转换代金券数据
|
|
|
+ if (statistics.getVoucherData() != null) {
|
|
|
+ StoreOperationalStatisticsVo.VoucherData voucher = statistics.getVoucherData();
|
|
|
+ Map<String, Object> voucherData = new HashMap<>();
|
|
|
+ voucherData.put("giveToFriendCount", voucher.getGiftToFriendsCount() != null ? voucher.getGiftToFriendsCount() : 0L);
|
|
|
+ voucherData.put("giveToFriendAmount", voucher.getGiftToFriendsAmount() != null ? voucher.getGiftToFriendsAmount() : BigDecimal.ZERO);
|
|
|
+ voucherData.put("giveToFriendUseCount", voucher.getGiftToFriendsUsedCount() != null ? voucher.getGiftToFriendsUsedCount() : 0L);
|
|
|
+ voucherData.put("giveToFriendUseAmount", voucher.getGiftToFriendsUsedAmount() != null ? voucher.getGiftToFriendsUsedAmount() : BigDecimal.ZERO);
|
|
|
+ if (voucher.getGiftToFriendsUsedAmountRatio() != null) {
|
|
|
+ voucherData.put("giveToFriendUseAmountPercent", voucher.getGiftToFriendsUsedAmountRatio().doubleValue());
|
|
|
+ } else {
|
|
|
+ voucherData.put("giveToFriendUseAmountPercent", 0.0);
|
|
|
+ }
|
|
|
+ voucherData.put("friendGiveCount", voucher.getFriendsGiftCount() != null ? voucher.getFriendsGiftCount() : 0L);
|
|
|
+ voucherData.put("friendGiveAmount", voucher.getFriendsGiftAmount() != null ? voucher.getFriendsGiftAmount() : BigDecimal.ZERO);
|
|
|
+ voucherData.put("friendGiveUseCount", voucher.getFriendsGiftUsedCount() != null ? voucher.getFriendsGiftUsedCount() : 0L);
|
|
|
+ voucherData.put("friendGiveUseAmount", voucher.getFriendsGiftUsedAmount() != null ? voucher.getFriendsGiftUsedAmount() : BigDecimal.ZERO);
|
|
|
+ if (voucher.getFriendsGiftUsedAmountRatio() != null) {
|
|
|
+ voucherData.put("friendGiveUseAmountPercent", voucher.getFriendsGiftUsedAmountRatio().doubleValue());
|
|
|
+ } else {
|
|
|
+ voucherData.put("friendGiveUseAmountPercent", 0.0);
|
|
|
+ }
|
|
|
+ result.put("voucherData", voucherData);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 5. 转换服务质量数据
|
|
|
+ if (statistics.getServiceQualityData() != null) {
|
|
|
+ StoreOperationalStatisticsVo.ServiceQualityData service = statistics.getServiceQualityData();
|
|
|
+ Map<String, Object> serviceData = new HashMap<>();
|
|
|
+ serviceData.put("storeScore", service.getStoreRating() != null ? service.getStoreRating().doubleValue() : 0.0);
|
|
|
+ serviceData.put("tasteScore", service.getTasteRating() != null ? service.getTasteRating().doubleValue() : 0.0);
|
|
|
+ serviceData.put("environmentScore", service.getEnvironmentRating() != null ? service.getEnvironmentRating().doubleValue() : 0.0);
|
|
|
+ serviceData.put("serviceScore", service.getServiceRating() != null ? service.getServiceRating().doubleValue() : 0.0);
|
|
|
+ serviceData.put("ratingCount", service.getTotalReviews() != null ? service.getTotalReviews() : 0L);
|
|
|
+ serviceData.put("goodRatingCount", service.getPositiveReviews() != null ? service.getPositiveReviews() : 0L);
|
|
|
+ serviceData.put("midRatingCount", service.getNeutralReviews() != null ? service.getNeutralReviews() : 0L);
|
|
|
+ serviceData.put("badRatingCount", service.getNegativeReviews() != null ? service.getNegativeReviews() : 0L);
|
|
|
+ if (service.getNegativeReviewRatio() != null) {
|
|
|
+ serviceData.put("badRatingPercent", service.getNegativeReviewRatio().doubleValue());
|
|
|
+ } else {
|
|
|
+ serviceData.put("badRatingPercent", 0.0);
|
|
|
+ }
|
|
|
+ serviceData.put("appealCount", service.getNegativeReviewAppealsCount() != null ? service.getNegativeReviewAppealsCount() : 0L);
|
|
|
+ serviceData.put("appealSuccessCount", service.getNegativeReviewAppealsSuccessCount() != null ? service.getNegativeReviewAppealsSuccessCount() : 0L);
|
|
|
+ if (service.getNegativeReviewAppealsSuccessRatio() != null) {
|
|
|
+ serviceData.put("appealSuccessPercent", service.getNegativeReviewAppealsSuccessRatio().doubleValue());
|
|
|
+ } else {
|
|
|
+ serviceData.put("appealSuccessPercent", 0.0);
|
|
|
+ }
|
|
|
+ result.put("serviceData", serviceData);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 6. 转换价目表排名数据
|
|
|
+ if (statistics.getPriceListRanking() != null && !statistics.getPriceListRanking().isEmpty()) {
|
|
|
+ List<Map<String, Object>> priceRankingData = new ArrayList<>();
|
|
|
+ for (StoreOperationalStatisticsVo.PriceListRanking ranking : statistics.getPriceListRanking()) {
|
|
|
+ Map<String, Object> priceItem = new HashMap<>();
|
|
|
+ priceItem.put("priceId", ranking.getPriceId() != null ? ranking.getPriceId() : 0);
|
|
|
+ priceItem.put("viewCount", ranking.getPageViews() != null ? ranking.getPageViews().intValue() : 0);
|
|
|
+ priceItem.put("visitorCount", ranking.getVisitors() != null ? ranking.getVisitors().intValue() : 0);
|
|
|
+ priceItem.put("shareCount", ranking.getShares() != null ? ranking.getShares().intValue() : 0);
|
|
|
+ priceRankingData.add(priceItem);
|
|
|
+ }
|
|
|
+ result.put("priceRankingData", priceRankingData);
|
|
|
+ } else {
|
|
|
+ result.put("priceRankingData", new ArrayList<>());
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -439,6 +610,7 @@ public class StoreOperationalStatisticsServiceImpl implements StoreOperationalSt
|
|
|
for (StorePrice price : prices) {
|
|
|
StoreOperationalStatisticsVo.PriceListRanking ranking = new StoreOperationalStatisticsVo.PriceListRanking();
|
|
|
ranking.setRank(rank++);
|
|
|
+ ranking.setPriceId(price.getId());
|
|
|
ranking.setPriceListItemName(price.getName());
|
|
|
// TODO: 统计该价目表的浏览量、访客数、分享数
|
|
|
ranking.setPageViews(0L);
|
|
|
@@ -456,6 +628,13 @@ public class StoreOperationalStatisticsServiceImpl implements StoreOperationalSt
|
|
|
private StoreOperationalStatisticsComparisonVo.TrafficDataComparison buildTrafficDataComparison(
|
|
|
StoreOperationalStatisticsVo.TrafficData current, StoreOperationalStatisticsVo.TrafficData previous) {
|
|
|
StoreOperationalStatisticsComparisonVo.TrafficDataComparison comparison = new StoreOperationalStatisticsComparisonVo.TrafficDataComparison();
|
|
|
+ // 如果 previous 为 null,创建空对象避免 NullPointerException
|
|
|
+ if (previous == null) {
|
|
|
+ previous = new StoreOperationalStatisticsVo.TrafficData();
|
|
|
+ }
|
|
|
+ if (current == null) {
|
|
|
+ current = new StoreOperationalStatisticsVo.TrafficData();
|
|
|
+ }
|
|
|
comparison.setStoreSearchVolume(buildComparisonData(current.getStoreSearchVolume(), previous.getStoreSearchVolume()));
|
|
|
comparison.setPageViews(buildComparisonData(current.getPageViews(), previous.getPageViews()));
|
|
|
comparison.setVisitors(buildComparisonData(current.getVisitors(), previous.getVisitors()));
|
|
|
@@ -471,6 +650,13 @@ public class StoreOperationalStatisticsServiceImpl implements StoreOperationalSt
|
|
|
private StoreOperationalStatisticsComparisonVo.InteractionDataComparison buildInteractionDataComparison(
|
|
|
StoreOperationalStatisticsVo.InteractionData current, StoreOperationalStatisticsVo.InteractionData previous) {
|
|
|
StoreOperationalStatisticsComparisonVo.InteractionDataComparison comparison = new StoreOperationalStatisticsComparisonVo.InteractionDataComparison();
|
|
|
+ // 如果 previous 为 null,创建空对象避免 NullPointerException
|
|
|
+ if (previous == null) {
|
|
|
+ previous = new StoreOperationalStatisticsVo.InteractionData();
|
|
|
+ }
|
|
|
+ if (current == null) {
|
|
|
+ current = new StoreOperationalStatisticsVo.InteractionData();
|
|
|
+ }
|
|
|
comparison.setStoreCollectionCount(buildComparisonData(current.getStoreCollectionCount(), previous.getStoreCollectionCount()));
|
|
|
comparison.setStoreShareCount(buildComparisonData(current.getStoreShareCount(), previous.getStoreShareCount()));
|
|
|
comparison.setStoreCheckInCount(buildComparisonData(current.getStoreCheckInCount(), previous.getStoreCheckInCount()));
|
|
|
@@ -493,6 +679,13 @@ public class StoreOperationalStatisticsServiceImpl implements StoreOperationalSt
|
|
|
private StoreOperationalStatisticsComparisonVo.CouponDataComparison buildCouponDataComparison(
|
|
|
StoreOperationalStatisticsVo.CouponData current, StoreOperationalStatisticsVo.CouponData previous) {
|
|
|
StoreOperationalStatisticsComparisonVo.CouponDataComparison comparison = new StoreOperationalStatisticsComparisonVo.CouponDataComparison();
|
|
|
+ // 如果 previous 为 null,创建空对象避免 NullPointerException
|
|
|
+ if (previous == null) {
|
|
|
+ previous = new StoreOperationalStatisticsVo.CouponData();
|
|
|
+ }
|
|
|
+ if (current == null) {
|
|
|
+ current = new StoreOperationalStatisticsVo.CouponData();
|
|
|
+ }
|
|
|
comparison.setGiftToFriendsCount(buildComparisonData(current.getGiftToFriendsCount(), previous.getGiftToFriendsCount()));
|
|
|
comparison.setGiftToFriendsAmount(buildComparisonData(current.getGiftToFriendsAmount(), previous.getGiftToFriendsAmount()));
|
|
|
comparison.setGiftToFriendsUsedCount(buildComparisonData(current.getGiftToFriendsUsedCount(), previous.getGiftToFriendsUsedCount()));
|
|
|
@@ -512,6 +705,13 @@ public class StoreOperationalStatisticsServiceImpl implements StoreOperationalSt
|
|
|
private StoreOperationalStatisticsComparisonVo.VoucherDataComparison buildVoucherDataComparison(
|
|
|
StoreOperationalStatisticsVo.VoucherData current, StoreOperationalStatisticsVo.VoucherData previous) {
|
|
|
StoreOperationalStatisticsComparisonVo.VoucherDataComparison comparison = new StoreOperationalStatisticsComparisonVo.VoucherDataComparison();
|
|
|
+ // 如果 previous 为 null,创建空对象避免 NullPointerException
|
|
|
+ if (previous == null) {
|
|
|
+ previous = new StoreOperationalStatisticsVo.VoucherData();
|
|
|
+ }
|
|
|
+ if (current == null) {
|
|
|
+ current = new StoreOperationalStatisticsVo.VoucherData();
|
|
|
+ }
|
|
|
comparison.setGiftToFriendsCount(buildComparisonData(current.getGiftToFriendsCount(), previous.getGiftToFriendsCount()));
|
|
|
comparison.setGiftToFriendsAmount(buildComparisonData(current.getGiftToFriendsAmount(), previous.getGiftToFriendsAmount()));
|
|
|
comparison.setGiftToFriendsUsedCount(buildComparisonData(current.getGiftToFriendsUsedCount(), previous.getGiftToFriendsUsedCount()));
|
|
|
@@ -531,6 +731,13 @@ public class StoreOperationalStatisticsServiceImpl implements StoreOperationalSt
|
|
|
private StoreOperationalStatisticsComparisonVo.ServiceQualityDataComparison buildServiceQualityDataComparison(
|
|
|
StoreOperationalStatisticsVo.ServiceQualityData current, StoreOperationalStatisticsVo.ServiceQualityData previous) {
|
|
|
StoreOperationalStatisticsComparisonVo.ServiceQualityDataComparison comparison = new StoreOperationalStatisticsComparisonVo.ServiceQualityDataComparison();
|
|
|
+ // 如果 previous 为 null,创建空对象避免 NullPointerException
|
|
|
+ if (previous == null) {
|
|
|
+ previous = new StoreOperationalStatisticsVo.ServiceQualityData();
|
|
|
+ }
|
|
|
+ if (current == null) {
|
|
|
+ current = new StoreOperationalStatisticsVo.ServiceQualityData();
|
|
|
+ }
|
|
|
comparison.setStoreRating(buildComparisonData(current.getStoreRating(), previous.getStoreRating()));
|
|
|
comparison.setTasteRating(buildComparisonData(current.getTasteRating(), previous.getTasteRating()));
|
|
|
comparison.setEnvironmentRating(buildComparisonData(current.getEnvironmentRating(), previous.getEnvironmentRating()));
|
|
|
@@ -607,6 +814,48 @@ public class StoreOperationalStatisticsServiceImpl implements StoreOperationalSt
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
+ public StoreOperationalStatisticsVo getStatisticsInTrackFormat(Integer storeId, String startTime, String endTime) {
|
|
|
+ log.info("StoreOperationalStatisticsServiceImpl.getStatisticsInTrackFormat - storeId={}, startTime={}, endTime={}",
|
|
|
+ storeId, startTime, endTime);
|
|
|
+
|
|
|
+ // 计算统计数据
|
|
|
+ StoreOperationalStatisticsVo statistics = calculateStatistics(storeId, startTime, endTime);
|
|
|
+
|
|
|
+ // 保存统计数据到历史表
|
|
|
+ saveStatisticsHistory(storeId, startTime, endTime, statistics);
|
|
|
+
|
|
|
+ return statistics;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 保存统计数据到历史表
|
|
|
+ */
|
|
|
+ private void saveStatisticsHistory(Integer storeId, String startTime, String endTime, StoreOperationalStatisticsVo statistics) {
|
|
|
+ try {
|
|
|
+ SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT);
|
|
|
+ Date startDate = sdf.parse(startTime);
|
|
|
+ Date endDate = sdf.parse(endTime);
|
|
|
+
|
|
|
+ StoreOperationalStatisticsHistory history = new StoreOperationalStatisticsHistory();
|
|
|
+ history.setStoreId(storeId);
|
|
|
+ history.setStartTime(startDate);
|
|
|
+ history.setEndTime(endDate);
|
|
|
+ history.setQueryTime(new Date());
|
|
|
+
|
|
|
+ // 将统计数据序列化为JSON字符串
|
|
|
+ String statisticsJson = JSON.toJSONString(statistics);
|
|
|
+ history.setStatisticsData(statisticsJson);
|
|
|
+
|
|
|
+ statisticsHistoryMapper.insert(history);
|
|
|
+ log.info("保存统计数据到历史表成功 - storeId={}, startTime={}, endTime={}", storeId, startTime, endTime);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("保存统计数据到历史表失败 - storeId={}, startTime={}, endTime={}, error={}",
|
|
|
+ storeId, startTime, endTime, e.getMessage(), e);
|
|
|
+ // 保存失败不影响主流程,只记录日志
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
public List<StoreOperationalStatisticsHistory> getHistoryList(Integer storeId) {
|
|
|
log.info("StoreOperationalStatisticsServiceImpl.getHistoryList - storeId={}", storeId);
|
|
|
|
|
|
@@ -660,4 +909,460 @@ public class StoreOperationalStatisticsServiceImpl implements StoreOperationalSt
|
|
|
return false;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ // ==================== 累加和转换方法 ====================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 累加流量数据
|
|
|
+ */
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ private void aggregateTrafficData(String trafficDataJson, Map<String, Long> accumulator) {
|
|
|
+ try {
|
|
|
+ Map<String, Object> data = (Map<String, Object>) JSON.parseObject(trafficDataJson, Map.class);
|
|
|
+ if (data == null) return;
|
|
|
+
|
|
|
+ // 累加各个字段
|
|
|
+ accumulator.put("searchCount", accumulator.getOrDefault("searchCount", 0L) +
|
|
|
+ getLongValue(data, "searchCount"));
|
|
|
+ accumulator.put("viewCount", accumulator.getOrDefault("viewCount", 0L) +
|
|
|
+ getLongValue(data, "viewCount"));
|
|
|
+ accumulator.put("visitorCount", accumulator.getOrDefault("visitorCount", 0L) +
|
|
|
+ getLongValue(data, "visitorCount"));
|
|
|
+ accumulator.put("newVisitorCount", accumulator.getOrDefault("newVisitorCount", 0L) +
|
|
|
+ getLongValue(data, "newVisitorCount"));
|
|
|
+ accumulator.put("totalDuration", accumulator.getOrDefault("totalDuration", 0L) +
|
|
|
+ getLongValue(data, "totalDuration"));
|
|
|
+ accumulator.put("avgDuration", accumulator.getOrDefault("avgDuration", 0L) +
|
|
|
+ getLongValue(data, "avgDuration"));
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("解析流量数据失败: {}", trafficDataJson, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 累加互动数据
|
|
|
+ */
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ private void aggregateInteractionData(String interactionDataJson, Map<String, Long> accumulator) {
|
|
|
+ try {
|
|
|
+ Map<String, Object> data = (Map<String, Object>) JSON.parseObject(interactionDataJson, Map.class);
|
|
|
+ if (data == null) return;
|
|
|
+
|
|
|
+ accumulator.put("collectCount", accumulator.getOrDefault("collectCount", 0L) +
|
|
|
+ getLongValue(data, "collectCount"));
|
|
|
+ accumulator.put("shareCount", accumulator.getOrDefault("shareCount", 0L) +
|
|
|
+ getLongValue(data, "shareCount"));
|
|
|
+ accumulator.put("checkinCount", accumulator.getOrDefault("checkinCount", 0L) +
|
|
|
+ getLongValue(data, "checkinCount"));
|
|
|
+ accumulator.put("consultCount", accumulator.getOrDefault("consultCount", 0L) +
|
|
|
+ getLongValue(data, "consultCount"));
|
|
|
+ accumulator.put("friendCount", accumulator.getOrDefault("friendCount", 0L) +
|
|
|
+ getLongValue(data, "friendCount"));
|
|
|
+ accumulator.put("followCount", accumulator.getOrDefault("followCount", 0L) +
|
|
|
+ getLongValue(data, "followCount"));
|
|
|
+ accumulator.put("fansCount", accumulator.getOrDefault("fansCount", 0L) +
|
|
|
+ getLongValue(data, "fansCount"));
|
|
|
+ accumulator.put("postCount", accumulator.getOrDefault("postCount", 0L) +
|
|
|
+ getLongValue(data, "postCount"));
|
|
|
+ accumulator.put("postLikeCount", accumulator.getOrDefault("postLikeCount", 0L) +
|
|
|
+ getLongValue(data, "postLikeCount"));
|
|
|
+ accumulator.put("postCommentCount", accumulator.getOrDefault("postCommentCount", 0L) +
|
|
|
+ getLongValue(data, "postCommentCount"));
|
|
|
+ accumulator.put("postRepostCount", accumulator.getOrDefault("postRepostCount", 0L) +
|
|
|
+ getLongValue(data, "postRepostCount"));
|
|
|
+ accumulator.put("reportCount", accumulator.getOrDefault("reportCount", 0L) +
|
|
|
+ getLongValue(data, "reportCount"));
|
|
|
+ accumulator.put("blockCount", accumulator.getOrDefault("blockCount", 0L) +
|
|
|
+ getLongValue(data, "blockCount"));
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("解析互动数据失败: {}", interactionDataJson, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 累加优惠券数据
|
|
|
+ */
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ private void aggregateCouponData(String couponDataJson, Map<String, Object> accumulator) {
|
|
|
+ try {
|
|
|
+ Map<String, Object> data = (Map<String, Object>) JSON.parseObject(couponDataJson, Map.class);
|
|
|
+ if (data == null) return;
|
|
|
+
|
|
|
+ // 累加Long类型字段
|
|
|
+ accumulator.put("giveToFriendCount", getLongValue(accumulator, "giveToFriendCount") +
|
|
|
+ getLongValue(data, "giveToFriendCount"));
|
|
|
+ accumulator.put("giveToFriendUseCount", getLongValue(accumulator, "giveToFriendUseCount") +
|
|
|
+ getLongValue(data, "giveToFriendUseCount"));
|
|
|
+ accumulator.put("friendGiveCount", getLongValue(accumulator, "friendGiveCount") +
|
|
|
+ getLongValue(data, "friendGiveCount"));
|
|
|
+ accumulator.put("friendGiveUseCount", getLongValue(accumulator, "friendGiveUseCount") +
|
|
|
+ getLongValue(data, "friendGiveUseCount"));
|
|
|
+
|
|
|
+ // 累加BigDecimal类型字段
|
|
|
+ accumulator.put("giveToFriendAmount", getBigDecimalValue(accumulator, "giveToFriendAmount")
|
|
|
+ .add(getBigDecimalValue(data, "giveToFriendAmount")));
|
|
|
+ accumulator.put("giveToFriendUseAmount", getBigDecimalValue(accumulator, "giveToFriendUseAmount")
|
|
|
+ .add(getBigDecimalValue(data, "giveToFriendUseAmount")));
|
|
|
+ accumulator.put("friendGiveAmount", getBigDecimalValue(accumulator, "friendGiveAmount")
|
|
|
+ .add(getBigDecimalValue(data, "friendGiveAmount")));
|
|
|
+ accumulator.put("friendGiveUseAmount", getBigDecimalValue(accumulator, "friendGiveUseAmount")
|
|
|
+ .add(getBigDecimalValue(data, "friendGiveUseAmount")));
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("解析优惠券数据失败: {}", couponDataJson, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 累加代金券数据
|
|
|
+ */
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ private void aggregateVoucherData(String voucherDataJson, Map<String, Object> accumulator) {
|
|
|
+ try {
|
|
|
+ Map<String, Object> data = (Map<String, Object>) JSON.parseObject(voucherDataJson, Map.class);
|
|
|
+ if (data == null) return;
|
|
|
+
|
|
|
+ // 累加Long类型字段
|
|
|
+ accumulator.put("giveToFriendCount", getLongValue(accumulator, "giveToFriendCount") +
|
|
|
+ getLongValue(data, "giveToFriendCount"));
|
|
|
+ accumulator.put("giveToFriendUseCount", getLongValue(accumulator, "giveToFriendUseCount") +
|
|
|
+ getLongValue(data, "giveToFriendUseCount"));
|
|
|
+ accumulator.put("friendGiveCount", getLongValue(accumulator, "friendGiveCount") +
|
|
|
+ getLongValue(data, "friendGiveCount"));
|
|
|
+ accumulator.put("friendGiveUseCount", getLongValue(accumulator, "friendGiveUseCount") +
|
|
|
+ getLongValue(data, "friendGiveUseCount"));
|
|
|
+
|
|
|
+ // 累加BigDecimal类型字段
|
|
|
+ accumulator.put("giveToFriendAmount", getBigDecimalValue(accumulator, "giveToFriendAmount")
|
|
|
+ .add(getBigDecimalValue(data, "giveToFriendAmount")));
|
|
|
+ accumulator.put("giveToFriendUseAmount", getBigDecimalValue(accumulator, "giveToFriendUseAmount")
|
|
|
+ .add(getBigDecimalValue(data, "giveToFriendUseAmount")));
|
|
|
+ accumulator.put("friendGiveAmount", getBigDecimalValue(accumulator, "friendGiveAmount")
|
|
|
+ .add(getBigDecimalValue(data, "friendGiveAmount")));
|
|
|
+ accumulator.put("friendGiveUseAmount", getBigDecimalValue(accumulator, "friendGiveUseAmount")
|
|
|
+ .add(getBigDecimalValue(data, "friendGiveUseAmount")));
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("解析代金券数据失败: {}", voucherDataJson, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 累加服务质量数据
|
|
|
+ */
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ private void aggregateServiceData(String serviceDataJson, Map<String, Object> accumulator) {
|
|
|
+ try {
|
|
|
+ Map<String, Object> data = (Map<String, Object>) JSON.parseObject(serviceDataJson, Map.class);
|
|
|
+ if (data == null) return;
|
|
|
+
|
|
|
+ // 累加评分(需要计算平均值)
|
|
|
+ accumulator.put("storeScoreSum", getBigDecimalValue(accumulator, "storeScoreSum")
|
|
|
+ .add(getBigDecimalValue(data, "storeScore")));
|
|
|
+ accumulator.put("tasteScoreSum", getBigDecimalValue(accumulator, "tasteScoreSum")
|
|
|
+ .add(getBigDecimalValue(data, "tasteScore")));
|
|
|
+ accumulator.put("environmentScoreSum", getBigDecimalValue(accumulator, "environmentScoreSum")
|
|
|
+ .add(getBigDecimalValue(data, "environmentScore")));
|
|
|
+ accumulator.put("serviceScoreSum", getBigDecimalValue(accumulator, "serviceScoreSum")
|
|
|
+ .add(getBigDecimalValue(data, "serviceScore")));
|
|
|
+
|
|
|
+ // 累加Long类型字段
|
|
|
+ accumulator.put("ratingCount", getLongValue(accumulator, "ratingCount") +
|
|
|
+ getLongValue(data, "ratingCount"));
|
|
|
+ accumulator.put("goodRatingCount", getLongValue(accumulator, "goodRatingCount") +
|
|
|
+ getLongValue(data, "goodRatingCount"));
|
|
|
+ accumulator.put("midRatingCount", getLongValue(accumulator, "midRatingCount") +
|
|
|
+ getLongValue(data, "midRatingCount"));
|
|
|
+ accumulator.put("badRatingCount", getLongValue(accumulator, "badRatingCount") +
|
|
|
+ getLongValue(data, "badRatingCount"));
|
|
|
+ accumulator.put("appealCount", getLongValue(accumulator, "appealCount") +
|
|
|
+ getLongValue(data, "appealCount"));
|
|
|
+ accumulator.put("appealSuccessCount", getLongValue(accumulator, "appealSuccessCount") +
|
|
|
+ getLongValue(data, "appealSuccessCount"));
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("解析服务质量数据失败: {}", serviceDataJson, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 累加价目表排名数据
|
|
|
+ */
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ private void aggregatePriceRankingData(String priceRankingDataJson, Map<Integer, Map<String, Long>> accumulator) {
|
|
|
+ try {
|
|
|
+ List<Map<String, Object>> dataList = (List<Map<String, Object>>) (List<?>) JSON.parseArray(priceRankingDataJson);
|
|
|
+ if (dataList == null) return;
|
|
|
+
|
|
|
+ for (Map<String, Object> item : dataList) {
|
|
|
+ Integer priceId = getIntegerValue(item, "priceId");
|
|
|
+ if (priceId == null) continue;
|
|
|
+
|
|
|
+ Map<String, Long> priceData = accumulator.computeIfAbsent(priceId, k -> new HashMap<>());
|
|
|
+ priceData.put("viewCount", priceData.getOrDefault("viewCount", 0L) +
|
|
|
+ getLongValue(item, "viewCount"));
|
|
|
+ priceData.put("visitorCount", priceData.getOrDefault("visitorCount", 0L) +
|
|
|
+ getLongValue(item, "visitorCount"));
|
|
|
+ priceData.put("shareCount", priceData.getOrDefault("shareCount", 0L) +
|
|
|
+ getLongValue(item, "shareCount"));
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("解析价目表排名数据失败: {}", priceRankingDataJson, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 转换为流量数据VO
|
|
|
+ */
|
|
|
+ private StoreOperationalStatisticsVo.TrafficData convertToTrafficDataVo(Map<String, Long> accumulator, int count) {
|
|
|
+ StoreOperationalStatisticsVo.TrafficData vo = new StoreOperationalStatisticsVo.TrafficData();
|
|
|
+ vo.setStoreSearchVolume(accumulator.getOrDefault("searchCount", 0L));
|
|
|
+ vo.setPageViews(accumulator.getOrDefault("viewCount", 0L));
|
|
|
+ vo.setVisitors(accumulator.getOrDefault("visitorCount", 0L));
|
|
|
+ vo.setNewVisitors(accumulator.getOrDefault("newVisitorCount", 0L));
|
|
|
+
|
|
|
+ // 访问时长转换为秒(原数据是毫秒)
|
|
|
+ Long totalDuration = accumulator.getOrDefault("totalDuration", 0L);
|
|
|
+ vo.setVisitDuration(totalDuration / 1000);
|
|
|
+
|
|
|
+ // 平均访问时长:如果有多个统计记录,需要重新计算平均值
|
|
|
+ Long avgDuration = accumulator.getOrDefault("avgDuration", 0L);
|
|
|
+ if (count > 0 && avgDuration > 0) {
|
|
|
+ vo.setAvgVisitDuration(avgDuration / 1000); // 转换为秒
|
|
|
+ } else {
|
|
|
+ vo.setAvgVisitDuration(0L);
|
|
|
+ }
|
|
|
+
|
|
|
+ return vo;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 转换为互动数据VO
|
|
|
+ */
|
|
|
+ private StoreOperationalStatisticsVo.InteractionData convertToInteractionDataVo(Map<String, Long> accumulator) {
|
|
|
+ StoreOperationalStatisticsVo.InteractionData vo = new StoreOperationalStatisticsVo.InteractionData();
|
|
|
+ vo.setStoreCollectionCount(accumulator.getOrDefault("collectCount", 0L));
|
|
|
+ vo.setStoreShareCount(accumulator.getOrDefault("shareCount", 0L));
|
|
|
+ vo.setStoreCheckInCount(accumulator.getOrDefault("checkinCount", 0L));
|
|
|
+ vo.setConsultMerchantCount(accumulator.getOrDefault("consultCount", 0L));
|
|
|
+ vo.setFriendsCount(accumulator.getOrDefault("friendCount", 0L));
|
|
|
+ vo.setFollowCount(accumulator.getOrDefault("followCount", 0L));
|
|
|
+ vo.setFansCount(accumulator.getOrDefault("fansCount", 0L));
|
|
|
+ vo.setPostsPublishedCount(accumulator.getOrDefault("postCount", 0L));
|
|
|
+ vo.setPostLikesCount(accumulator.getOrDefault("postLikeCount", 0L));
|
|
|
+ vo.setPostCommentsCount(accumulator.getOrDefault("postCommentCount", 0L));
|
|
|
+ vo.setPostSharesCount(accumulator.getOrDefault("postRepostCount", 0L));
|
|
|
+ vo.setReportedCount(accumulator.getOrDefault("reportCount", 0L));
|
|
|
+ vo.setBlockedCount(accumulator.getOrDefault("blockCount", 0L));
|
|
|
+ return vo;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 转换为优惠券数据VO
|
|
|
+ */
|
|
|
+ private StoreOperationalStatisticsVo.CouponData convertToCouponDataVo(Map<String, Object> accumulator) {
|
|
|
+ StoreOperationalStatisticsVo.CouponData vo = new StoreOperationalStatisticsVo.CouponData();
|
|
|
+ vo.setGiftToFriendsCount(getLongValue(accumulator, "giveToFriendCount"));
|
|
|
+ vo.setGiftToFriendsAmount(getBigDecimalValue(accumulator, "giveToFriendAmount"));
|
|
|
+ vo.setGiftToFriendsUsedCount(getLongValue(accumulator, "giveToFriendUseCount"));
|
|
|
+ vo.setGiftToFriendsUsedAmount(getBigDecimalValue(accumulator, "giveToFriendUseAmount"));
|
|
|
+
|
|
|
+ // 计算使用金额占比
|
|
|
+ BigDecimal giftAmount = getBigDecimalValue(accumulator, "giveToFriendAmount");
|
|
|
+ BigDecimal giftUsedAmount = getBigDecimalValue(accumulator, "giveToFriendUseAmount");
|
|
|
+ if (giftAmount.compareTo(BigDecimal.ZERO) > 0) {
|
|
|
+ vo.setGiftToFriendsUsedAmountRatio(giftUsedAmount.divide(giftAmount, 4, RoundingMode.HALF_UP)
|
|
|
+ .multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP));
|
|
|
+ } else {
|
|
|
+ vo.setGiftToFriendsUsedAmountRatio(BigDecimal.ZERO);
|
|
|
+ }
|
|
|
+
|
|
|
+ vo.setFriendsGiftCount(getLongValue(accumulator, "friendGiveCount"));
|
|
|
+ vo.setFriendsGiftAmount(getBigDecimalValue(accumulator, "friendGiveAmount"));
|
|
|
+ vo.setFriendsGiftUsedCount(getLongValue(accumulator, "friendGiveUseCount"));
|
|
|
+ vo.setFriendsGiftUsedAmount(getBigDecimalValue(accumulator, "friendGiveUseAmount"));
|
|
|
+
|
|
|
+ // 计算好友赠送使用金额占比
|
|
|
+ BigDecimal friendAmount = getBigDecimalValue(accumulator, "friendGiveAmount");
|
|
|
+ BigDecimal friendUsedAmount = getBigDecimalValue(accumulator, "friendGiveUseAmount");
|
|
|
+ if (friendAmount.compareTo(BigDecimal.ZERO) > 0) {
|
|
|
+ vo.setFriendsGiftUsedAmountRatio(friendUsedAmount.divide(friendAmount, 4, RoundingMode.HALF_UP)
|
|
|
+ .multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP));
|
|
|
+ } else {
|
|
|
+ vo.setFriendsGiftUsedAmountRatio(BigDecimal.ZERO);
|
|
|
+ }
|
|
|
+
|
|
|
+ return vo;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 转换为代金券数据VO
|
|
|
+ */
|
|
|
+ private StoreOperationalStatisticsVo.VoucherData convertToVoucherDataVo(Map<String, Object> accumulator) {
|
|
|
+ StoreOperationalStatisticsVo.VoucherData vo = new StoreOperationalStatisticsVo.VoucherData();
|
|
|
+ vo.setGiftToFriendsCount(getLongValue(accumulator, "giveToFriendCount"));
|
|
|
+ vo.setGiftToFriendsAmount(getBigDecimalValue(accumulator, "giveToFriendAmount"));
|
|
|
+ vo.setGiftToFriendsUsedCount(getLongValue(accumulator, "giveToFriendUseCount"));
|
|
|
+ vo.setGiftToFriendsUsedAmount(getBigDecimalValue(accumulator, "giveToFriendUseAmount"));
|
|
|
+
|
|
|
+ // 计算使用金额占比
|
|
|
+ BigDecimal giftAmount = getBigDecimalValue(accumulator, "giveToFriendAmount");
|
|
|
+ BigDecimal giftUsedAmount = getBigDecimalValue(accumulator, "giveToFriendUseAmount");
|
|
|
+ if (giftAmount.compareTo(BigDecimal.ZERO) > 0) {
|
|
|
+ vo.setGiftToFriendsUsedAmountRatio(giftUsedAmount.divide(giftAmount, 4, RoundingMode.HALF_UP)
|
|
|
+ .multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP));
|
|
|
+ } else {
|
|
|
+ vo.setGiftToFriendsUsedAmountRatio(BigDecimal.ZERO);
|
|
|
+ }
|
|
|
+
|
|
|
+ vo.setFriendsGiftCount(getLongValue(accumulator, "friendGiveCount"));
|
|
|
+ vo.setFriendsGiftAmount(getBigDecimalValue(accumulator, "friendGiveAmount"));
|
|
|
+ vo.setFriendsGiftUsedCount(getLongValue(accumulator, "friendGiveUseCount"));
|
|
|
+ vo.setFriendsGiftUsedAmount(getBigDecimalValue(accumulator, "friendGiveUseAmount"));
|
|
|
+
|
|
|
+ // 计算好友赠送使用金额占比
|
|
|
+ BigDecimal friendAmount = getBigDecimalValue(accumulator, "friendGiveAmount");
|
|
|
+ BigDecimal friendUsedAmount = getBigDecimalValue(accumulator, "friendGiveUseAmount");
|
|
|
+ if (friendAmount.compareTo(BigDecimal.ZERO) > 0) {
|
|
|
+ vo.setFriendsGiftUsedAmountRatio(friendUsedAmount.divide(friendAmount, 4, RoundingMode.HALF_UP)
|
|
|
+ .multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP));
|
|
|
+ } else {
|
|
|
+ vo.setFriendsGiftUsedAmountRatio(BigDecimal.ZERO);
|
|
|
+ }
|
|
|
+
|
|
|
+ return vo;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 转换为服务质量数据VO
|
|
|
+ */
|
|
|
+ private StoreOperationalStatisticsVo.ServiceQualityData convertToServiceQualityDataVo(Map<String, Object> accumulator, int count) {
|
|
|
+ StoreOperationalStatisticsVo.ServiceQualityData vo = new StoreOperationalStatisticsVo.ServiceQualityData();
|
|
|
+
|
|
|
+ // 计算平均评分
|
|
|
+ if (count > 0) {
|
|
|
+ BigDecimal storeScoreSum = getBigDecimalValue(accumulator, "storeScoreSum");
|
|
|
+ BigDecimal tasteScoreSum = getBigDecimalValue(accumulator, "tasteScoreSum");
|
|
|
+ BigDecimal environmentScoreSum = getBigDecimalValue(accumulator, "environmentScoreSum");
|
|
|
+ BigDecimal serviceScoreSum = getBigDecimalValue(accumulator, "serviceScoreSum");
|
|
|
+
|
|
|
+ vo.setStoreRating(storeScoreSum.divide(BigDecimal.valueOf(count), 2, RoundingMode.HALF_UP));
|
|
|
+ vo.setTasteRating(tasteScoreSum.divide(BigDecimal.valueOf(count), 2, RoundingMode.HALF_UP));
|
|
|
+ vo.setEnvironmentRating(environmentScoreSum.divide(BigDecimal.valueOf(count), 2, RoundingMode.HALF_UP));
|
|
|
+ vo.setServiceRating(serviceScoreSum.divide(BigDecimal.valueOf(count), 2, RoundingMode.HALF_UP));
|
|
|
+ } else {
|
|
|
+ vo.setStoreRating(BigDecimal.ZERO);
|
|
|
+ vo.setTasteRating(BigDecimal.ZERO);
|
|
|
+ vo.setEnvironmentRating(BigDecimal.ZERO);
|
|
|
+ vo.setServiceRating(BigDecimal.ZERO);
|
|
|
+ }
|
|
|
+
|
|
|
+ vo.setTotalReviews(getLongValue(accumulator, "ratingCount"));
|
|
|
+ vo.setPositiveReviews(getLongValue(accumulator, "goodRatingCount"));
|
|
|
+ vo.setNeutralReviews(getLongValue(accumulator, "midRatingCount"));
|
|
|
+ vo.setNegativeReviews(getLongValue(accumulator, "badRatingCount"));
|
|
|
+
|
|
|
+ // 计算差评占比
|
|
|
+ Long ratingCount = getLongValue(accumulator, "ratingCount");
|
|
|
+ Long badRatingCount = getLongValue(accumulator, "badRatingCount");
|
|
|
+ if (ratingCount > 0) {
|
|
|
+ vo.setNegativeReviewRatio(BigDecimal.valueOf(badRatingCount)
|
|
|
+ .divide(BigDecimal.valueOf(ratingCount), 4, RoundingMode.HALF_UP)
|
|
|
+ .multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP));
|
|
|
+ } else {
|
|
|
+ vo.setNegativeReviewRatio(BigDecimal.ZERO);
|
|
|
+ }
|
|
|
+
|
|
|
+ vo.setNegativeReviewAppealsCount(getLongValue(accumulator, "appealCount"));
|
|
|
+ vo.setNegativeReviewAppealsSuccessCount(getLongValue(accumulator, "appealSuccessCount"));
|
|
|
+
|
|
|
+ // 计算差评申诉成功占比
|
|
|
+ Long appealCount = getLongValue(accumulator, "appealCount");
|
|
|
+ Long appealSuccessCount = getLongValue(accumulator, "appealSuccessCount");
|
|
|
+ if (appealCount > 0) {
|
|
|
+ vo.setNegativeReviewAppealsSuccessRatio(BigDecimal.valueOf(appealSuccessCount)
|
|
|
+ .divide(BigDecimal.valueOf(appealCount), 4, RoundingMode.HALF_UP)
|
|
|
+ .multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP));
|
|
|
+ } else {
|
|
|
+ vo.setNegativeReviewAppealsSuccessRatio(BigDecimal.ZERO);
|
|
|
+ }
|
|
|
+
|
|
|
+ return vo;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 转换为价目表排名数据VO
|
|
|
+ */
|
|
|
+ private List<StoreOperationalStatisticsVo.PriceListRanking> convertToPriceListRankingVo(Map<Integer, Map<String, Long>> accumulator) {
|
|
|
+ List<StoreOperationalStatisticsVo.PriceListRanking> result = new ArrayList<>();
|
|
|
+
|
|
|
+ // 转换为列表并按浏览量降序排序
|
|
|
+ for (Map.Entry<Integer, Map<String, Long>> entry : accumulator.entrySet()) {
|
|
|
+ StoreOperationalStatisticsVo.PriceListRanking ranking = new StoreOperationalStatisticsVo.PriceListRanking();
|
|
|
+ ranking.setPriceId(entry.getKey());
|
|
|
+ Map<String, Long> data = entry.getValue();
|
|
|
+ ranking.setPageViews(data.getOrDefault("viewCount", 0L));
|
|
|
+ ranking.setVisitors(data.getOrDefault("visitorCount", 0L));
|
|
|
+ ranking.setShares(data.getOrDefault("shareCount", 0L));
|
|
|
+
|
|
|
+ // 获取价目表名称(如果需要)
|
|
|
+ // ranking.setPriceListItemName(...);
|
|
|
+
|
|
|
+ result.add(ranking);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 按浏览量降序排序
|
|
|
+ result.sort((a, b) -> Long.compare(b.getPageViews(), a.getPageViews()));
|
|
|
+
|
|
|
+ // 设置排名
|
|
|
+ for (int i = 0; i < result.size(); i++) {
|
|
|
+ result.get(i).setRank(i + 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 工具方法 ====================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从Map中获取Long值
|
|
|
+ */
|
|
|
+ private Long getLongValue(Map<String, Object> map, String key) {
|
|
|
+ Object value = map.get(key);
|
|
|
+ if (value == null) return 0L;
|
|
|
+ if (value instanceof Long) return (Long) value;
|
|
|
+ if (value instanceof Number) return ((Number) value).longValue();
|
|
|
+ try {
|
|
|
+ return Long.parseLong(value.toString());
|
|
|
+ } catch (Exception e) {
|
|
|
+ return 0L;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从Map中获取Integer值
|
|
|
+ */
|
|
|
+ private Integer getIntegerValue(Map<String, Object> map, String key) {
|
|
|
+ Object value = map.get(key);
|
|
|
+ if (value == null) return null;
|
|
|
+ if (value instanceof Integer) return (Integer) value;
|
|
|
+ if (value instanceof Number) return ((Number) value).intValue();
|
|
|
+ try {
|
|
|
+ return Integer.parseInt(value.toString());
|
|
|
+ } catch (Exception e) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从Map中获取BigDecimal值
|
|
|
+ */
|
|
|
+ private BigDecimal getBigDecimalValue(Map<String, Object> map, String key) {
|
|
|
+ Object value = map.get(key);
|
|
|
+ if (value == null) return BigDecimal.ZERO;
|
|
|
+ if (value instanceof BigDecimal) return (BigDecimal) value;
|
|
|
+ if (value instanceof Number) return BigDecimal.valueOf(((Number) value).doubleValue());
|
|
|
+ try {
|
|
|
+ return new BigDecimal(value.toString());
|
|
|
+ } catch (Exception e) {
|
|
|
+ return BigDecimal.ZERO;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|