|
|
@@ -114,8 +114,8 @@ public class StoreOperationalStatisticsServiceImpl implements StoreOperationalSt
|
|
|
return new StoreOperationalStatisticsVo();
|
|
|
}
|
|
|
|
|
|
- // 累加所有统计数据
|
|
|
- return aggregateStatistics(statisticsList);
|
|
|
+ // 聚合统计数据(优先取结束日期的数据,只累加新增访客数)
|
|
|
+ return aggregateStatistics(statisticsList, endDate);
|
|
|
|
|
|
} catch (ParseException e) {
|
|
|
log.error("StoreOperationalStatisticsServiceImpl.calculateStatistics - 时间解析错误", e);
|
|
|
@@ -128,65 +128,127 @@ public class StoreOperationalStatisticsServiceImpl implements StoreOperationalSt
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 累加统计数据
|
|
|
+ * 聚合统计数据(优先取结束日期的数据,只累加流量数据中的新增访客数,其他数据取结束日期的记录)
|
|
|
+ *
|
|
|
+ * @param statisticsList 统计数据列表
|
|
|
+ * @param endDate 结束日期(用于优先选择该日期的数据)
|
|
|
*/
|
|
|
- private StoreOperationalStatisticsVo aggregateStatistics(List<StoreTrackStatistics> statisticsList) {
|
|
|
+ private StoreOperationalStatisticsVo aggregateStatistics(List<StoreTrackStatistics> statisticsList, Date endDate) {
|
|
|
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;
|
|
|
-
|
|
|
- // 遍历所有统计数据并累加
|
|
|
+ // 优先查找结束日期的数据(比较日期部分,忽略时间)
|
|
|
+ StoreTrackStatistics endDateStat = null;
|
|
|
+ SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT);
|
|
|
+ String endDateStr = sdf.format(endDate);
|
|
|
for (StoreTrackStatistics stat : statisticsList) {
|
|
|
- // 累加流量数据
|
|
|
- if (stat.getTrafficData() != null && !stat.getTrafficData().isEmpty()) {
|
|
|
- aggregateTrafficData(stat.getTrafficData(), trafficAccumulator);
|
|
|
- trafficCount++;
|
|
|
+ if (stat.getStatDate() != null) {
|
|
|
+ String statDateStr = sdf.format(stat.getStatDate());
|
|
|
+ if (endDateStr.equals(statDateStr)) {
|
|
|
+ endDateStat = stat;
|
|
|
+ break;
|
|
|
+ }
|
|
|
}
|
|
|
+ }
|
|
|
+
|
|
|
+ // 判断结束日期的数据是否有效(不全为0)
|
|
|
+ boolean endDateStatValid = false;
|
|
|
+ if (endDateStat != null) {
|
|
|
+ endDateStatValid = isStatisticsValid(endDateStat);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果没有结束日期的数据,或者结束日期的数据全是0,则按日期排序,取有数据的最新日期
|
|
|
+ StoreTrackStatistics latestStat = null;
|
|
|
+ if (endDateStat != null && endDateStatValid) {
|
|
|
+ latestStat = endDateStat;
|
|
|
+ } else {
|
|
|
+ // 按日期排序,从最新到最旧
|
|
|
+ statisticsList.sort((a, b) -> {
|
|
|
+ if (a.getStatDate() == null && b.getStatDate() == null) return 0;
|
|
|
+ if (a.getStatDate() == null) return 1;
|
|
|
+ if (b.getStatDate() == null) return -1;
|
|
|
+ return b.getStatDate().compareTo(a.getStatDate());
|
|
|
+ });
|
|
|
|
|
|
- // 累加互动数据
|
|
|
- if (stat.getInteractionData() != null && !stat.getInteractionData().isEmpty()) {
|
|
|
- aggregateInteractionData(stat.getInteractionData(), interactionAccumulator);
|
|
|
+ // 查找第一个有数据的记录
|
|
|
+ for (StoreTrackStatistics stat : statisticsList) {
|
|
|
+ if (isStatisticsValid(stat)) {
|
|
|
+ latestStat = stat;
|
|
|
+ break;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- // 累加优惠券数据
|
|
|
- if (stat.getCouponData() != null && !stat.getCouponData().isEmpty()) {
|
|
|
- aggregateCouponData(stat.getCouponData(), couponAccumulator);
|
|
|
+ // 如果所有记录都无效,使用最新的记录(即使全是0)
|
|
|
+ if (latestStat == null && !statisticsList.isEmpty()) {
|
|
|
+ latestStat = statisticsList.get(0);
|
|
|
}
|
|
|
|
|
|
- // 累加代金券数据
|
|
|
- if (stat.getVoucherData() != null && !stat.getVoucherData().isEmpty()) {
|
|
|
- aggregateVoucherData(stat.getVoucherData(), voucherAccumulator);
|
|
|
+ if (latestStat != null) {
|
|
|
+ if (endDateStat != null && !endDateStatValid) {
|
|
|
+ log.info("结束日期{}的数据全是0,使用范围内有数据的最新日期{}的数据",
|
|
|
+ endDateStr,
|
|
|
+ latestStat.getStatDate() != null ? sdf.format(latestStat.getStatDate()) : "未知");
|
|
|
+ } else {
|
|
|
+ log.info("结束日期{}没有统计数据,使用范围内最新日期{}的数据",
|
|
|
+ endDateStr,
|
|
|
+ latestStat.getStatDate() != null ? sdf.format(latestStat.getStatDate()) : "未知");
|
|
|
+ }
|
|
|
}
|
|
|
-
|
|
|
- // 累加服务质量数据
|
|
|
- if (stat.getServiceData() != null && !stat.getServiceData().isEmpty()) {
|
|
|
- aggregateServiceData(stat.getServiceData(), serviceAccumulator);
|
|
|
- serviceCount++;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果找不到有效数据,返回空结果
|
|
|
+ if (latestStat == null) {
|
|
|
+ log.warn("未找到有效的统计数据,返回空结果");
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 只累加流量数据中的新增访客数
|
|
|
+ long newVisitorCountSum = 0L;
|
|
|
+ for (StoreTrackStatistics stat : statisticsList) {
|
|
|
+ if (stat.getTrafficData() != null && !stat.getTrafficData().isEmpty()) {
|
|
|
+ try {
|
|
|
+ Map<String, Object> trafficData = (Map<String, Object>) JSON.parseObject(stat.getTrafficData(), Map.class);
|
|
|
+ if (trafficData != null && trafficData.containsKey("newVisitorCount")) {
|
|
|
+ newVisitorCountSum += getLongValue(trafficData, "newVisitorCount");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("解析流量数据失败: {}", stat.getTrafficData(), e);
|
|
|
+ }
|
|
|
}
|
|
|
-
|
|
|
- // 累加价目表排名数据
|
|
|
- if (stat.getPriceRankingData() != null && !stat.getPriceRankingData().isEmpty()) {
|
|
|
- aggregatePriceRankingData(stat.getPriceRankingData(), priceRankingAccumulator);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 使用最新记录的数据,但替换新增访客数为累加值
|
|
|
+ if (latestStat.getTrafficData() != null && !latestStat.getTrafficData().isEmpty()) {
|
|
|
+ try {
|
|
|
+ Map<String, Object> latestTrafficData = (Map<String, Object>) JSON.parseObject(latestStat.getTrafficData(), Map.class);
|
|
|
+ if (latestTrafficData != null) {
|
|
|
+ latestTrafficData.put("newVisitorCount", newVisitorCountSum);
|
|
|
+ result.setTrafficData(convertToTrafficDataVoFromMap(latestTrafficData));
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("解析最新流量数据失败: {}", latestStat.getTrafficData(), e);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 转换为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));
|
|
|
+ // 其他数据直接使用最新记录的值
|
|
|
+ if (latestStat.getInteractionData() != null && !latestStat.getInteractionData().isEmpty()) {
|
|
|
+ result.setInteractionData(convertToInteractionDataVoFromJson(latestStat.getInteractionData()));
|
|
|
+ }
|
|
|
+
|
|
|
+ if (latestStat.getCouponData() != null && !latestStat.getCouponData().isEmpty()) {
|
|
|
+ result.setCouponData(convertToCouponDataVoFromJson(latestStat.getCouponData()));
|
|
|
+ }
|
|
|
+
|
|
|
+ if (latestStat.getVoucherData() != null && !latestStat.getVoucherData().isEmpty()) {
|
|
|
+ result.setVoucherData(convertToVoucherDataVoFromJson(latestStat.getVoucherData()));
|
|
|
+ }
|
|
|
+
|
|
|
+ if (latestStat.getServiceData() != null && !latestStat.getServiceData().isEmpty()) {
|
|
|
+ result.setServiceQualityData(convertToServiceQualityDataVoFromJson(latestStat.getServiceData()));
|
|
|
+ }
|
|
|
+
|
|
|
+ if (latestStat.getPriceRankingData() != null && !latestStat.getPriceRankingData().isEmpty()) {
|
|
|
+ result.setPriceListRanking(convertToPriceListRankingVoFromJson(latestStat.getPriceRankingData()));
|
|
|
+ }
|
|
|
|
|
|
return result;
|
|
|
}
|
|
|
@@ -236,6 +298,9 @@ public class StoreOperationalStatisticsServiceImpl implements StoreOperationalSt
|
|
|
Integer historyId = saveStatisticsHistory(storeId, currentStartTime, currentEndTime,
|
|
|
previousStartTime, previousEndTime, comparison, null);
|
|
|
|
|
|
+ // 将历史记录ID设置到返回对象中
|
|
|
+ comparison.setHistoryId(historyId);
|
|
|
+
|
|
|
// 异步调用AI接口进行数据分析
|
|
|
if (historyId != null) {
|
|
|
try {
|
|
|
@@ -1655,6 +1720,121 @@ public class StoreOperationalStatisticsServiceImpl implements StoreOperationalSt
|
|
|
// ==================== 工具方法 ====================
|
|
|
|
|
|
/**
|
|
|
+ * 判断统计数据是否有效(不全为0)
|
|
|
+ * 检查流量数据、互动数据、优惠券数据、代金券数据、服务质量数据、价目表排名数据中是否至少有一个字段不为0
|
|
|
+ */
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ private boolean isStatisticsValid(StoreTrackStatistics stat) {
|
|
|
+ if (stat == null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 检查流量数据
|
|
|
+ if (stat.getTrafficData() != null && !stat.getTrafficData().isEmpty()) {
|
|
|
+ Map<String, Object> trafficData = (Map<String, Object>) JSON.parseObject(stat.getTrafficData(), Map.class);
|
|
|
+ if (trafficData != null) {
|
|
|
+ long searchCount = getLongValue(trafficData, "searchCount");
|
|
|
+ long viewCount = getLongValue(trafficData, "viewCount");
|
|
|
+ long visitorCount = getLongValue(trafficData, "visitorCount");
|
|
|
+ long newVisitorCount = getLongValue(trafficData, "newVisitorCount");
|
|
|
+ long totalDuration = getLongValue(trafficData, "totalDuration");
|
|
|
+ if (searchCount > 0 || viewCount > 0 || visitorCount > 0 || newVisitorCount > 0 || totalDuration > 0) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查互动数据
|
|
|
+ if (stat.getInteractionData() != null && !stat.getInteractionData().isEmpty()) {
|
|
|
+ Map<String, Object> interactionData = (Map<String, Object>) JSON.parseObject(stat.getInteractionData(), Map.class);
|
|
|
+ if (interactionData != null) {
|
|
|
+ long collectCount = getLongValue(interactionData, "collectCount");
|
|
|
+ long shareCount = getLongValue(interactionData, "shareCount");
|
|
|
+ long checkinCount = getLongValue(interactionData, "checkinCount");
|
|
|
+ long consultCount = getLongValue(interactionData, "consultCount");
|
|
|
+ long friendCount = getLongValue(interactionData, "friendCount");
|
|
|
+ long followCount = getLongValue(interactionData, "followCount");
|
|
|
+ long fansCount = getLongValue(interactionData, "fansCount");
|
|
|
+ long postCount = getLongValue(interactionData, "postCount");
|
|
|
+ long postLikeCount = getLongValue(interactionData, "postLikeCount");
|
|
|
+ long postCommentCount = getLongValue(interactionData, "postCommentCount");
|
|
|
+ long postRepostCount = getLongValue(interactionData, "postRepostCount");
|
|
|
+ if (collectCount > 0 || shareCount > 0 || checkinCount > 0 || consultCount > 0 ||
|
|
|
+ friendCount > 0 || followCount > 0 || fansCount > 0 || postCount > 0 ||
|
|
|
+ postLikeCount > 0 || postCommentCount > 0 || postRepostCount > 0) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查优惠券数据
|
|
|
+ if (stat.getCouponData() != null && !stat.getCouponData().isEmpty()) {
|
|
|
+ Map<String, Object> couponData = (Map<String, Object>) JSON.parseObject(stat.getCouponData(), Map.class);
|
|
|
+ if (couponData != null) {
|
|
|
+ long giveToFriendCount = getLongValue(couponData, "giveToFriendCount");
|
|
|
+ BigDecimal giveToFriendAmount = getBigDecimalValue(couponData, "giveToFriendAmount");
|
|
|
+ long friendGiveCount = getLongValue(couponData, "friendGiveCount");
|
|
|
+ BigDecimal friendGiveAmount = getBigDecimalValue(couponData, "friendGiveAmount");
|
|
|
+ if (giveToFriendCount > 0 || giveToFriendAmount.compareTo(BigDecimal.ZERO) > 0 ||
|
|
|
+ friendGiveCount > 0 || friendGiveAmount.compareTo(BigDecimal.ZERO) > 0) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查代金券数据
|
|
|
+ if (stat.getVoucherData() != null && !stat.getVoucherData().isEmpty()) {
|
|
|
+ Map<String, Object> voucherData = (Map<String, Object>) JSON.parseObject(stat.getVoucherData(), Map.class);
|
|
|
+ if (voucherData != null) {
|
|
|
+ long giveToFriendCount = getLongValue(voucherData, "giveToFriendCount");
|
|
|
+ BigDecimal giveToFriendAmount = getBigDecimalValue(voucherData, "giveToFriendAmount");
|
|
|
+ long friendGiveCount = getLongValue(voucherData, "friendGiveCount");
|
|
|
+ BigDecimal friendGiveAmount = getBigDecimalValue(voucherData, "friendGiveAmount");
|
|
|
+ if (giveToFriendCount > 0 || giveToFriendAmount.compareTo(BigDecimal.ZERO) > 0 ||
|
|
|
+ friendGiveCount > 0 || friendGiveAmount.compareTo(BigDecimal.ZERO) > 0) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查服务质量数据
|
|
|
+ if (stat.getServiceData() != null && !stat.getServiceData().isEmpty()) {
|
|
|
+ Map<String, Object> serviceData = (Map<String, Object>) JSON.parseObject(stat.getServiceData(), Map.class);
|
|
|
+ if (serviceData != null) {
|
|
|
+ double storeScore = getBigDecimalValue(serviceData, "storeScore").doubleValue();
|
|
|
+ long ratingCount = getLongValue(serviceData, "ratingCount");
|
|
|
+ long appealCount = getLongValue(serviceData, "appealCount");
|
|
|
+ if (storeScore > 0 || ratingCount > 0 || appealCount > 0) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查价目表排名数据
|
|
|
+ if (stat.getPriceRankingData() != null && !stat.getPriceRankingData().isEmpty()) {
|
|
|
+ List<Map<String, Object>> priceRankingData = (List<Map<String, Object>>) (List<?>) JSON.parseArray(stat.getPriceRankingData());
|
|
|
+ if (priceRankingData != null && !priceRankingData.isEmpty()) {
|
|
|
+ for (Map<String, Object> item : priceRankingData) {
|
|
|
+ long viewCount = getLongValue(item, "viewCount");
|
|
|
+ long visitorCount = getLongValue(item, "visitorCount");
|
|
|
+ long shareCount = getLongValue(item, "shareCount");
|
|
|
+ if (viewCount > 0 || visitorCount > 0 || shareCount > 0) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("判断统计数据有效性失败: {}", e.getMessage(), e);
|
|
|
+ // 如果解析失败,认为数据无效
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
* 从Map中获取Long值
|
|
|
*/
|
|
|
private Long getLongValue(Map<String, Object> map, String key) {
|
|
|
@@ -1698,6 +1878,318 @@ public class StoreOperationalStatisticsServiceImpl implements StoreOperationalSt
|
|
|
return BigDecimal.ZERO;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ // ==================== 从JSON/Map直接转换的方法 ====================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从Map转换为流量数据VO
|
|
|
+ */
|
|
|
+ private StoreOperationalStatisticsVo.TrafficData convertToTrafficDataVoFromMap(Map<String, Object> trafficDataMap) {
|
|
|
+ StoreOperationalStatisticsVo.TrafficData vo = new StoreOperationalStatisticsVo.TrafficData();
|
|
|
+ vo.setStoreSearchVolume(getLongValue(trafficDataMap, "searchCount"));
|
|
|
+ vo.setPageViews(getLongValue(trafficDataMap, "viewCount"));
|
|
|
+ vo.setVisitors(getLongValue(trafficDataMap, "visitorCount"));
|
|
|
+ vo.setNewVisitors(getLongValue(trafficDataMap, "newVisitorCount"));
|
|
|
+ // 访问时长从毫秒转换为秒
|
|
|
+ Long totalDuration = getLongValue(trafficDataMap, "totalDuration");
|
|
|
+ vo.setVisitDuration(totalDuration / 1000);
|
|
|
+ Long avgDuration = getLongValue(trafficDataMap, "avgDuration");
|
|
|
+ vo.setAvgVisitDuration(avgDuration / 1000);
|
|
|
+ return vo;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从JSON字符串转换为互动数据VO
|
|
|
+ */
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ private StoreOperationalStatisticsVo.InteractionData convertToInteractionDataVoFromJson(String interactionDataJson) {
|
|
|
+ try {
|
|
|
+ Map<String, Object> data = (Map<String, Object>) JSON.parseObject(interactionDataJson, Map.class);
|
|
|
+ if (data == null) return new StoreOperationalStatisticsVo.InteractionData();
|
|
|
+
|
|
|
+ StoreOperationalStatisticsVo.InteractionData vo = new StoreOperationalStatisticsVo.InteractionData();
|
|
|
+ vo.setStoreCollectionCount(getLongValue(data, "collectCount"));
|
|
|
+ vo.setStoreShareCount(getLongValue(data, "shareCount"));
|
|
|
+ vo.setStoreCheckInCount(getLongValue(data, "checkinCount"));
|
|
|
+ vo.setConsultMerchantCount(getLongValue(data, "consultCount"));
|
|
|
+ vo.setFriendsCount(getLongValue(data, "friendCount"));
|
|
|
+ vo.setFollowCount(getLongValue(data, "followCount"));
|
|
|
+ vo.setFansCount(getLongValue(data, "fansCount"));
|
|
|
+ vo.setPostsPublishedCount(getLongValue(data, "postCount"));
|
|
|
+ vo.setPostLikesCount(getLongValue(data, "postLikeCount"));
|
|
|
+ vo.setPostCommentsCount(getLongValue(data, "postCommentCount"));
|
|
|
+ vo.setPostSharesCount(getLongValue(data, "postRepostCount"));
|
|
|
+ vo.setReportedCount(getLongValue(data, "reportCount"));
|
|
|
+ vo.setBlockedCount(getLongValue(data, "blockCount"));
|
|
|
+ return vo;
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("解析互动数据失败: {}", interactionDataJson, e);
|
|
|
+ return new StoreOperationalStatisticsVo.InteractionData();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从JSON字符串转换为优惠券数据VO
|
|
|
+ */
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ private StoreOperationalStatisticsVo.CouponData convertToCouponDataVoFromJson(String couponDataJson) {
|
|
|
+ try {
|
|
|
+ Map<String, Object> data = (Map<String, Object>) JSON.parseObject(couponDataJson, Map.class);
|
|
|
+ if (data == null) return new StoreOperationalStatisticsVo.CouponData();
|
|
|
+
|
|
|
+ StoreOperationalStatisticsVo.CouponData vo = new StoreOperationalStatisticsVo.CouponData();
|
|
|
+ vo.setGiftToFriendsCount(getLongValue(data, "giveToFriendCount"));
|
|
|
+ vo.setGiftToFriendsAmount(getBigDecimalValue(data, "giveToFriendAmount"));
|
|
|
+ vo.setGiftToFriendsUsedCount(getLongValue(data, "giveToFriendUseCount"));
|
|
|
+ vo.setGiftToFriendsUsedAmount(getBigDecimalValue(data, "giveToFriendUseAmount"));
|
|
|
+
|
|
|
+ // 使用金额占比
|
|
|
+ Object useAmountPercent = data.get("giveToFriendUseAmountPercent");
|
|
|
+ if (useAmountPercent != null) {
|
|
|
+ if (useAmountPercent instanceof Number) {
|
|
|
+ vo.setGiftToFriendsUsedAmountRatio(BigDecimal.valueOf(((Number) useAmountPercent).doubleValue()));
|
|
|
+ } else {
|
|
|
+ vo.setGiftToFriendsUsedAmountRatio(new BigDecimal(useAmountPercent.toString()));
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ vo.setGiftToFriendsUsedAmountRatio(BigDecimal.ZERO);
|
|
|
+ }
|
|
|
+
|
|
|
+ vo.setFriendsGiftCount(getLongValue(data, "friendGiveCount"));
|
|
|
+ vo.setFriendsGiftAmount(getBigDecimalValue(data, "friendGiveAmount"));
|
|
|
+ vo.setFriendsGiftUsedCount(getLongValue(data, "friendGiveUseCount"));
|
|
|
+ vo.setFriendsGiftUsedAmount(getBigDecimalValue(data, "friendGiveUseAmount"));
|
|
|
+
|
|
|
+ // 好友赠送使用金额占比
|
|
|
+ Object friendUseAmountPercent = data.get("friendGiveUseAmountPercent");
|
|
|
+ if (friendUseAmountPercent != null) {
|
|
|
+ if (friendUseAmountPercent instanceof Number) {
|
|
|
+ vo.setFriendsGiftUsedAmountRatio(BigDecimal.valueOf(((Number) friendUseAmountPercent).doubleValue()));
|
|
|
+ } else {
|
|
|
+ vo.setFriendsGiftUsedAmountRatio(new BigDecimal(friendUseAmountPercent.toString()));
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ vo.setFriendsGiftUsedAmountRatio(BigDecimal.ZERO);
|
|
|
+ }
|
|
|
+
|
|
|
+ return vo;
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("解析优惠券数据失败: {}", couponDataJson, e);
|
|
|
+ return new StoreOperationalStatisticsVo.CouponData();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从JSON字符串转换为代金券数据VO
|
|
|
+ */
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ private StoreOperationalStatisticsVo.VoucherData convertToVoucherDataVoFromJson(String voucherDataJson) {
|
|
|
+ try {
|
|
|
+ Map<String, Object> data = (Map<String, Object>) JSON.parseObject(voucherDataJson, Map.class);
|
|
|
+ if (data == null) return new StoreOperationalStatisticsVo.VoucherData();
|
|
|
+
|
|
|
+ StoreOperationalStatisticsVo.VoucherData vo = new StoreOperationalStatisticsVo.VoucherData();
|
|
|
+ vo.setGiftToFriendsCount(getLongValue(data, "giveToFriendCount"));
|
|
|
+ vo.setGiftToFriendsAmount(getBigDecimalValue(data, "giveToFriendAmount"));
|
|
|
+ vo.setGiftToFriendsUsedCount(getLongValue(data, "giveToFriendUseCount"));
|
|
|
+ vo.setGiftToFriendsUsedAmount(getBigDecimalValue(data, "giveToFriendUseAmount"));
|
|
|
+
|
|
|
+ // 使用金额占比
|
|
|
+ Object useAmountPercent = data.get("giveToFriendUseAmountPercent");
|
|
|
+ if (useAmountPercent != null) {
|
|
|
+ if (useAmountPercent instanceof Number) {
|
|
|
+ vo.setGiftToFriendsUsedAmountRatio(BigDecimal.valueOf(((Number) useAmountPercent).doubleValue()));
|
|
|
+ } else {
|
|
|
+ vo.setGiftToFriendsUsedAmountRatio(new BigDecimal(useAmountPercent.toString()));
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ vo.setGiftToFriendsUsedAmountRatio(BigDecimal.ZERO);
|
|
|
+ }
|
|
|
+
|
|
|
+ vo.setFriendsGiftCount(getLongValue(data, "friendGiveCount"));
|
|
|
+ vo.setFriendsGiftAmount(getBigDecimalValue(data, "friendGiveAmount"));
|
|
|
+ vo.setFriendsGiftUsedCount(getLongValue(data, "friendGiveUseCount"));
|
|
|
+ vo.setFriendsGiftUsedAmount(getBigDecimalValue(data, "friendGiveUseAmount"));
|
|
|
+
|
|
|
+ // 好友赠送使用金额占比
|
|
|
+ Object friendUseAmountPercent = data.get("friendGiveUseAmountPercent");
|
|
|
+ if (friendUseAmountPercent != null) {
|
|
|
+ if (friendUseAmountPercent instanceof Number) {
|
|
|
+ vo.setFriendsGiftUsedAmountRatio(BigDecimal.valueOf(((Number) friendUseAmountPercent).doubleValue()));
|
|
|
+ } else {
|
|
|
+ vo.setFriendsGiftUsedAmountRatio(new BigDecimal(friendUseAmountPercent.toString()));
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ vo.setFriendsGiftUsedAmountRatio(BigDecimal.ZERO);
|
|
|
+ }
|
|
|
+
|
|
|
+ return vo;
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("解析代金券数据失败: {}", voucherDataJson, e);
|
|
|
+ return new StoreOperationalStatisticsVo.VoucherData();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从JSON字符串转换为服务质量数据VO
|
|
|
+ */
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ private StoreOperationalStatisticsVo.ServiceQualityData convertToServiceQualityDataVoFromJson(String serviceDataJson) {
|
|
|
+ try {
|
|
|
+ Map<String, Object> data = (Map<String, Object>) JSON.parseObject(serviceDataJson, Map.class);
|
|
|
+ if (data == null) return new StoreOperationalStatisticsVo.ServiceQualityData();
|
|
|
+
|
|
|
+ StoreOperationalStatisticsVo.ServiceQualityData vo = new StoreOperationalStatisticsVo.ServiceQualityData();
|
|
|
+
|
|
|
+ // 评分字段
|
|
|
+ Object storeScore = data.get("storeScore");
|
|
|
+ if (storeScore != null) {
|
|
|
+ if (storeScore instanceof Number) {
|
|
|
+ vo.setStoreRating(BigDecimal.valueOf(((Number) storeScore).doubleValue()).setScale(1, RoundingMode.HALF_UP));
|
|
|
+ } else {
|
|
|
+ vo.setStoreRating(new BigDecimal(storeScore.toString()).setScale(1, RoundingMode.HALF_UP));
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ vo.setStoreRating(BigDecimal.ZERO);
|
|
|
+ }
|
|
|
+
|
|
|
+ Object scoreOne = data.get("scoreOne");
|
|
|
+ if (scoreOne != null) {
|
|
|
+ if (scoreOne instanceof Number) {
|
|
|
+ vo.setScoreOne(BigDecimal.valueOf(((Number) scoreOne).doubleValue()).setScale(1, RoundingMode.HALF_UP));
|
|
|
+ } else {
|
|
|
+ vo.setScoreOne(new BigDecimal(scoreOne.toString()).setScale(1, RoundingMode.HALF_UP));
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ vo.setScoreOne(BigDecimal.ZERO);
|
|
|
+ }
|
|
|
+
|
|
|
+ Object scoreTwo = data.get("scoreTwo");
|
|
|
+ if (scoreTwo != null) {
|
|
|
+ if (scoreTwo instanceof Number) {
|
|
|
+ vo.setScoreTwo(BigDecimal.valueOf(((Number) scoreTwo).doubleValue()).setScale(1, RoundingMode.HALF_UP));
|
|
|
+ } else {
|
|
|
+ vo.setScoreTwo(new BigDecimal(scoreTwo.toString()).setScale(1, RoundingMode.HALF_UP));
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ vo.setScoreTwo(BigDecimal.ZERO);
|
|
|
+ }
|
|
|
+
|
|
|
+ Object scoreThree = data.get("scoreThree");
|
|
|
+ if (scoreThree != null) {
|
|
|
+ if (scoreThree instanceof Number) {
|
|
|
+ vo.setScoreThree(BigDecimal.valueOf(((Number) scoreThree).doubleValue()).setScale(1, RoundingMode.HALF_UP));
|
|
|
+ } else {
|
|
|
+ vo.setScoreThree(new BigDecimal(scoreThree.toString()).setScale(1, RoundingMode.HALF_UP));
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ vo.setScoreThree(BigDecimal.ZERO);
|
|
|
+ }
|
|
|
+
|
|
|
+ vo.setTotalReviews(getLongValue(data, "ratingCount"));
|
|
|
+ vo.setPositiveReviews(getLongValue(data, "goodRatingCount"));
|
|
|
+ vo.setNeutralReviews(getLongValue(data, "midRatingCount"));
|
|
|
+ vo.setNegativeReviews(getLongValue(data, "badRatingCount"));
|
|
|
+
|
|
|
+ // 差评占比
|
|
|
+ Object badRatingPercent = data.get("badRatingPercent");
|
|
|
+ if (badRatingPercent != null) {
|
|
|
+ if (badRatingPercent instanceof Number) {
|
|
|
+ vo.setNegativeReviewRatio(BigDecimal.valueOf(((Number) badRatingPercent).doubleValue()).setScale(2, RoundingMode.HALF_UP));
|
|
|
+ } else {
|
|
|
+ vo.setNegativeReviewRatio(new BigDecimal(badRatingPercent.toString()).setScale(2, RoundingMode.HALF_UP));
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ vo.setNegativeReviewRatio(BigDecimal.ZERO);
|
|
|
+ }
|
|
|
+
|
|
|
+ vo.setNegativeReviewAppealsCount(getLongValue(data, "appealCount"));
|
|
|
+ vo.setNegativeReviewAppealsSuccessCount(getLongValue(data, "appealSuccessCount"));
|
|
|
+
|
|
|
+ // 申诉成功占比
|
|
|
+ Object appealSuccessPercent = data.get("appealSuccessPercent");
|
|
|
+ if (appealSuccessPercent != null) {
|
|
|
+ if (appealSuccessPercent instanceof Number) {
|
|
|
+ vo.setNegativeReviewAppealsSuccessRatio(BigDecimal.valueOf(((Number) appealSuccessPercent).doubleValue()).setScale(2, RoundingMode.HALF_UP));
|
|
|
+ } else {
|
|
|
+ vo.setNegativeReviewAppealsSuccessRatio(new BigDecimal(appealSuccessPercent.toString()).setScale(2, RoundingMode.HALF_UP));
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ vo.setNegativeReviewAppealsSuccessRatio(BigDecimal.ZERO);
|
|
|
+ }
|
|
|
+
|
|
|
+ return vo;
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("解析服务质量数据失败: {}", serviceDataJson, e);
|
|
|
+ return new StoreOperationalStatisticsVo.ServiceQualityData();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从JSON字符串转换为价目表排名数据VO
|
|
|
+ */
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ private List<StoreOperationalStatisticsVo.PriceListRanking> convertToPriceListRankingVoFromJson(String priceRankingDataJson) {
|
|
|
+ List<StoreOperationalStatisticsVo.PriceListRanking> result = new ArrayList<>();
|
|
|
+
|
|
|
+ try {
|
|
|
+ List<Map<String, Object>> dataList = (List<Map<String, Object>>) (List<?>) JSON.parseArray(priceRankingDataJson);
|
|
|
+ if (dataList == null || dataList.isEmpty()) {
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 批量查询价目表名称
|
|
|
+ List<Integer> priceIds = new ArrayList<>();
|
|
|
+ for (Map<String, Object> item : dataList) {
|
|
|
+ Integer priceId = getIntegerValue(item, "priceId");
|
|
|
+ if (priceId != null) {
|
|
|
+ priceIds.add(priceId);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Map<Integer, String> priceNameMap = new HashMap<>();
|
|
|
+ if (!priceIds.isEmpty()) {
|
|
|
+ LambdaQueryWrapper<StorePrice> priceWrapper = new LambdaQueryWrapper<>();
|
|
|
+ priceWrapper.in(StorePrice::getId, priceIds)
|
|
|
+ .eq(StorePrice::getDeleteFlag, 0);
|
|
|
+ List<StorePrice> prices = storePriceMapper.selectList(priceWrapper);
|
|
|
+ for (StorePrice price : prices) {
|
|
|
+ if (price.getId() != null && price.getName() != null) {
|
|
|
+ priceNameMap.put(price.getId(), price.getName());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 转换为VO列表
|
|
|
+ for (Map<String, Object> item : dataList) {
|
|
|
+ StoreOperationalStatisticsVo.PriceListRanking ranking = new StoreOperationalStatisticsVo.PriceListRanking();
|
|
|
+ Integer priceId = getIntegerValue(item, "priceId");
|
|
|
+ ranking.setPriceId(priceId);
|
|
|
+ ranking.setPageViews(getLongValue(item, "viewCount"));
|
|
|
+ ranking.setVisitors(getLongValue(item, "visitorCount"));
|
|
|
+ ranking.setShares(getLongValue(item, "shareCount"));
|
|
|
+
|
|
|
+ if (priceId != null) {
|
|
|
+ ranking.setPriceListItemName(priceNameMap.get(priceId));
|
|
|
+ }
|
|
|
+
|
|
|
+ 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);
|
|
|
+ }
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("解析价目表排名数据失败: {}", priceRankingDataJson, e);
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
|
|
|
@Override
|
|
|
public String generateStatisticsComparisonPdf(Integer storeId, String currentStartTime, String currentEndTime,
|