|
@@ -0,0 +1,574 @@
|
|
|
|
|
+package shop.alien.store.service.impl;
|
|
|
|
|
+
|
|
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
|
|
+import lombok.RequiredArgsConstructor;
|
|
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
|
|
+import shop.alien.entity.store.*;
|
|
|
|
|
+import shop.alien.entity.store.vo.StoreOperationalStatisticsComparisonVo;
|
|
|
|
|
+import shop.alien.entity.store.vo.StoreOperationalStatisticsVo;
|
|
|
|
|
+import shop.alien.mapper.*;
|
|
|
|
|
+import shop.alien.store.service.StoreOperationalStatisticsService;
|
|
|
|
|
+
|
|
|
|
|
+import java.math.BigDecimal;
|
|
|
|
|
+import java.math.RoundingMode;
|
|
|
|
|
+import java.text.ParseException;
|
|
|
|
|
+import java.text.SimpleDateFormat;
|
|
|
|
|
+import java.util.*;
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 商家经营数据统计服务实现类
|
|
|
|
|
+ *
|
|
|
|
|
+ * @author system
|
|
|
|
|
+ * @version 1.0
|
|
|
|
|
+ * @date 2026/01/05
|
|
|
|
|
+ */
|
|
|
|
|
+@Slf4j
|
|
|
|
|
+@Service
|
|
|
|
|
+@RequiredArgsConstructor
|
|
|
|
|
+public class StoreOperationalStatisticsServiceImpl implements StoreOperationalStatisticsService {
|
|
|
|
|
+
|
|
|
|
|
+ private final LifeBrowseRecordMapper lifeBrowseRecordMapper;
|
|
|
|
|
+ private final LifeCollectMapper lifeCollectMapper;
|
|
|
|
|
+ private final StoreClockInMapper storeClockInMapper;
|
|
|
|
|
+ private final LifeUserDynamicsMapper lifeUserDynamicsMapper;
|
|
|
|
|
+ private final StoreCommentMapper storeCommentMapper;
|
|
|
|
|
+ private final LifeCommentMapper lifeCommentMapper;
|
|
|
|
|
+ private final LifeLikeRecordMapper lifeLikeRecordMapper;
|
|
|
|
|
+ private final LifeFansMapper lifeFansMapper;
|
|
|
|
|
+ private final LifeBlacklistMapper lifeBlacklistMapper;
|
|
|
|
|
+ private final LifeDiscountCouponStoreFriendMapper lifeDiscountCouponStoreFriendMapper;
|
|
|
|
|
+ private final LifeDiscountCouponUserMapper lifeDiscountCouponUserMapper;
|
|
|
|
|
+ private final LifeCouponMapper lifeCouponMapper;
|
|
|
|
|
+ private final CommonRatingMapper commonRatingMapper;
|
|
|
|
|
+ private final StoreEvaluationMapper storeEvaluationMapper;
|
|
|
|
|
+ private final StoreCommentAppealMapper storeCommentAppealMapper;
|
|
|
|
|
+ private final StorePriceMapper storePriceMapper;
|
|
|
|
|
+ private final StoreUserMapper storeUserMapper;
|
|
|
|
|
+
|
|
|
|
|
+ private static final String DATE_FORMAT = "yyyy-MM-dd";
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public StoreOperationalStatisticsVo getStatistics(Integer storeId, String startTime, String endTime) {
|
|
|
|
|
+ log.info("StoreOperationalStatisticsServiceImpl.getStatistics - storeId={}, startTime={}, endTime={}", storeId, startTime, endTime);
|
|
|
|
|
+
|
|
|
|
|
+ StoreOperationalStatisticsVo statistics = new StoreOperationalStatisticsVo();
|
|
|
|
|
+
|
|
|
|
|
+ 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));
|
|
|
|
|
+
|
|
|
|
|
+ } catch (ParseException e) {
|
|
|
|
|
+ log.error("StoreOperationalStatisticsServiceImpl.getStatistics - 时间解析错误", e);
|
|
|
|
|
+ throw new RuntimeException("时间格式错误: " + e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return statistics;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public StoreOperationalStatisticsComparisonVo getStatisticsComparison(Integer storeId, String currentStartTime, String currentEndTime,
|
|
|
|
|
+ String previousStartTime, String previousEndTime) {
|
|
|
|
|
+ log.info("StoreOperationalStatisticsServiceImpl.getStatisticsComparison - storeId={}, currentStartTime={}, currentEndTime={}, previousStartTime={}, previousEndTime={}",
|
|
|
|
|
+ storeId, currentStartTime, currentEndTime, previousStartTime, previousEndTime);
|
|
|
|
|
+
|
|
|
|
|
+ StoreOperationalStatisticsComparisonVo comparison = new StoreOperationalStatisticsComparisonVo();
|
|
|
|
|
+ comparison.setCurrentStartTime(currentStartTime);
|
|
|
|
|
+ comparison.setCurrentEndTime(currentEndTime);
|
|
|
|
|
+ comparison.setPreviousStartTime(previousStartTime);
|
|
|
|
|
+ comparison.setPreviousEndTime(previousEndTime);
|
|
|
|
|
+
|
|
|
|
|
+ // 获取当期和上期的统计数据
|
|
|
|
|
+ StoreOperationalStatisticsVo currentStatistics = getStatistics(storeId, currentStartTime, currentEndTime);
|
|
|
|
|
+ StoreOperationalStatisticsVo previousStatistics = getStatistics(storeId, previousStartTime, previousEndTime);
|
|
|
|
|
+
|
|
|
|
|
+ // 构建对比数据
|
|
|
|
|
+ comparison.setTrafficData(buildTrafficDataComparison(currentStatistics.getTrafficData(), previousStatistics.getTrafficData()));
|
|
|
|
|
+ comparison.setInteractionData(buildInteractionDataComparison(currentStatistics.getInteractionData(), previousStatistics.getInteractionData()));
|
|
|
|
|
+ comparison.setCouponData(buildCouponDataComparison(currentStatistics.getCouponData(), previousStatistics.getCouponData()));
|
|
|
|
|
+ comparison.setVoucherData(buildVoucherDataComparison(currentStatistics.getVoucherData(), previousStatistics.getVoucherData()));
|
|
|
|
|
+ comparison.setServiceQualityData(buildServiceQualityDataComparison(currentStatistics.getServiceQualityData(), previousStatistics.getServiceQualityData()));
|
|
|
|
|
+
|
|
|
|
|
+ return comparison;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取流量数据
|
|
|
|
|
+ */
|
|
|
|
|
+ private StoreOperationalStatisticsVo.TrafficData getTrafficData(Integer storeId, Date startDate, Date endDate) {
|
|
|
|
|
+ StoreOperationalStatisticsVo.TrafficData trafficData = new StoreOperationalStatisticsVo.TrafficData();
|
|
|
|
|
+
|
|
|
|
|
+ // TODO: 实现店铺搜索量统计(需要根据实际业务逻辑查询搜索记录表)
|
|
|
|
|
+ trafficData.setStoreSearchVolume(0L);
|
|
|
|
|
+
|
|
|
|
|
+ // 浏览记录统计
|
|
|
|
|
+ LambdaQueryWrapper<LifeBrowseRecord> browseWrapper = new LambdaQueryWrapper<>();
|
|
|
|
|
+ browseWrapper.eq(LifeBrowseRecord::getStoreId, String.valueOf(storeId))
|
|
|
|
|
+ .between(LifeBrowseRecord::getCreatedTime, startDate, endDate)
|
|
|
|
|
+ .eq(LifeBrowseRecord::getDeleteFlag, 0);
|
|
|
|
|
+ List<LifeBrowseRecord> browseRecords = lifeBrowseRecordMapper.selectList(browseWrapper);
|
|
|
|
|
+
|
|
|
|
|
+ // 浏览量
|
|
|
|
|
+ trafficData.setPageViews((long) browseRecords.size());
|
|
|
|
|
+
|
|
|
|
|
+ // 访客数(去重用户ID)
|
|
|
|
|
+ long visitors = browseRecords.stream()
|
|
|
|
|
+ .map(LifeBrowseRecord::getUserId)
|
|
|
|
|
+ .distinct()
|
|
|
|
|
+ .count();
|
|
|
|
|
+ trafficData.setVisitors(visitors);
|
|
|
|
|
+
|
|
|
|
|
+ // TODO: 新增访客数统计(需要对比历史数据判断是否为新访客)
|
|
|
|
|
+ trafficData.setNewVisitors(0L);
|
|
|
|
|
+
|
|
|
|
|
+ // TODO: 访问时长统计(需要根据实际业务逻辑计算)
|
|
|
|
|
+ trafficData.setVisitDuration(0L);
|
|
|
|
|
+
|
|
|
|
|
+ // TODO: 平均访问时长统计
|
|
|
|
|
+ trafficData.setAvgVisitDuration(0L);
|
|
|
|
|
+
|
|
|
|
|
+ return trafficData;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取互动数据
|
|
|
|
|
+ */
|
|
|
|
|
+ private StoreOperationalStatisticsVo.InteractionData getInteractionData(Integer storeId, Date startDate, Date endDate) {
|
|
|
|
|
+ StoreOperationalStatisticsVo.InteractionData interactionData = new StoreOperationalStatisticsVo.InteractionData();
|
|
|
|
|
+
|
|
|
|
|
+ // 收藏次数
|
|
|
|
|
+ LambdaQueryWrapper<LifeCollect> collectWrapper = new LambdaQueryWrapper<>();
|
|
|
|
|
+ collectWrapper.eq(LifeCollect::getStoreId, String.valueOf(storeId))
|
|
|
|
|
+ .between(LifeCollect::getCreatedTime, startDate, endDate)
|
|
|
|
|
+ .eq(LifeCollect::getDeleteFlag, 0);
|
|
|
|
|
+ long collectionCount = lifeCollectMapper.selectCount(collectWrapper);
|
|
|
|
|
+ interactionData.setStoreCollectionCount(collectionCount);
|
|
|
|
|
+
|
|
|
|
|
+ // TODO: 分享次数统计(需要根据实际业务逻辑查询分享记录表)
|
|
|
|
|
+ interactionData.setStoreShareCount(0L);
|
|
|
|
|
+
|
|
|
|
|
+ // 打卡次数
|
|
|
|
|
+ LambdaQueryWrapper<StoreClockIn> clockInWrapper = new LambdaQueryWrapper<>();
|
|
|
|
|
+ clockInWrapper.eq(StoreClockIn::getStoreId, storeId)
|
|
|
|
|
+ .between(StoreClockIn::getCreatedTime, startDate, endDate);
|
|
|
|
|
+ long checkInCount = storeClockInMapper.selectCount(clockInWrapper);
|
|
|
|
|
+ interactionData.setStoreCheckInCount(checkInCount);
|
|
|
|
|
+
|
|
|
|
|
+ // TODO: 咨询商家次数统计(需要根据实际业务逻辑查询咨询记录表)
|
|
|
|
|
+ interactionData.setConsultMerchantCount(0L);
|
|
|
|
|
+
|
|
|
|
|
+ // 好友数量(店铺好友关系)
|
|
|
|
|
+ // TODO: 需要根据实际业务逻辑查询好友关系表
|
|
|
|
|
+ interactionData.setFriendsCount(0L);
|
|
|
|
|
+
|
|
|
|
|
+ // 关注数量
|
|
|
|
|
+ // TODO: 需要根据实际业务逻辑查询关注关系(需要确认店铺的关注关系表结构)
|
|
|
|
|
+ // LambdaQueryWrapper<LifeFans> fansWrapper = new LambdaQueryWrapper<>();
|
|
|
|
|
+ interactionData.setFollowCount(0L);
|
|
|
|
|
+
|
|
|
|
|
+ // 粉丝数量
|
|
|
|
|
+ // TODO: 需要根据实际业务逻辑查询粉丝关系
|
|
|
|
|
+ interactionData.setFansCount(0L);
|
|
|
|
|
+
|
|
|
|
|
+ // 动态相关统计(店铺发布的动态)
|
|
|
|
|
+ StoreUser storeUser = storeUserMapper.selectOne(
|
|
|
|
|
+ new LambdaQueryWrapper<StoreUser>().eq(StoreUser::getStoreId, storeId).last("LIMIT 1"));
|
|
|
|
|
+ if (storeUser != null) {
|
|
|
|
|
+ String phoneId = "store_" + storeUser.getPhone();
|
|
|
|
|
+
|
|
|
|
|
+ // 发布动态数量
|
|
|
|
|
+ LambdaQueryWrapper<LifeUserDynamics> dynamicsWrapper = new LambdaQueryWrapper<>();
|
|
|
|
|
+ dynamicsWrapper.eq(LifeUserDynamics::getPhoneId, phoneId)
|
|
|
|
|
+ .between(LifeUserDynamics::getCreatedTime, startDate, endDate)
|
|
|
|
|
+ .eq(LifeUserDynamics::getDeleteFlag, 0)
|
|
|
|
|
+ .eq(LifeUserDynamics::getDraft, 0);
|
|
|
|
|
+ long postsCount = lifeUserDynamicsMapper.selectCount(dynamicsWrapper);
|
|
|
|
|
+ interactionData.setPostsPublishedCount(postsCount);
|
|
|
|
|
+
|
|
|
|
|
+ // 动态点赞数量
|
|
|
|
|
+ List<LifeUserDynamics> dynamicsList = lifeUserDynamicsMapper.selectList(dynamicsWrapper);
|
|
|
|
|
+ long likesCount = dynamicsList.stream()
|
|
|
|
|
+ .mapToLong(d -> d.getDianzanCount() != null ? d.getDianzanCount() : 0L)
|
|
|
|
|
+ .sum();
|
|
|
|
|
+ interactionData.setPostLikesCount(likesCount);
|
|
|
|
|
+
|
|
|
|
|
+ // 动态评论数量
|
|
|
|
|
+ long commentsCount = 0L;
|
|
|
|
|
+ for (LifeUserDynamics dynamics : dynamicsList) {
|
|
|
|
|
+ LambdaQueryWrapper<StoreComment> commentWrapper = new LambdaQueryWrapper<>();
|
|
|
|
|
+ commentWrapper.eq(StoreComment::getBusinessId, String.valueOf(dynamics.getId()))
|
|
|
|
|
+ .eq(StoreComment::getBusinessType, 2)
|
|
|
|
|
+ .eq(StoreComment::getDeleteFlag, 0);
|
|
|
|
|
+ commentsCount += storeCommentMapper.selectCount(commentWrapper);
|
|
|
|
|
+ }
|
|
|
|
|
+ interactionData.setPostCommentsCount(commentsCount);
|
|
|
|
|
+
|
|
|
|
|
+ // 动态转发数量
|
|
|
|
|
+ long sharesCount = dynamicsList.stream()
|
|
|
|
|
+ .mapToLong(d -> d.getTransferCount() != null ? d.getTransferCount() : 0L)
|
|
|
|
|
+ .sum();
|
|
|
|
|
+ interactionData.setPostSharesCount(sharesCount);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ interactionData.setPostsPublishedCount(0L);
|
|
|
|
|
+ interactionData.setPostLikesCount(0L);
|
|
|
|
|
+ interactionData.setPostCommentsCount(0L);
|
|
|
|
|
+ interactionData.setPostSharesCount(0L);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // TODO: 被举报次数统计
|
|
|
|
|
+ interactionData.setReportedCount(0L);
|
|
|
|
|
+
|
|
|
|
|
+ // 被拉黑次数
|
|
|
|
|
+ // TODO: 需要根据实际业务逻辑查询拉黑记录
|
|
|
|
|
+ interactionData.setBlockedCount(0L);
|
|
|
|
|
+
|
|
|
|
|
+ return interactionData;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取优惠券数据
|
|
|
|
|
+ */
|
|
|
|
|
+ private StoreOperationalStatisticsVo.CouponData getCouponData(Integer storeId, Date startDate, Date endDate) {
|
|
|
|
|
+ StoreOperationalStatisticsVo.CouponData couponData = new StoreOperationalStatisticsVo.CouponData();
|
|
|
|
|
+
|
|
|
|
|
+ // TODO: 实现优惠券相关统计
|
|
|
|
|
+ // 1. 赠送好友数量
|
|
|
|
|
+ // 2. 赠送好友金额合计
|
|
|
|
|
+ // 3. 赠送好友使用数量
|
|
|
|
|
+ // 4. 赠送好友使用金额合计
|
|
|
|
|
+ // 5. 好友赠送数量
|
|
|
|
|
+ // 6. 好友赠送金额合计
|
|
|
|
|
+ // 7. 好友赠送使用数量
|
|
|
|
|
+ // 8. 好友赠送使用金额合计
|
|
|
|
|
+
|
|
|
|
|
+ // TODO: 根据实际业务逻辑查询 life_discount_coupon_store_friend 表
|
|
|
|
|
+ // 注意:LifeDiscountCouponStoreFriend 没有 storeId 字段,需要通过 storeUserId 关联查询
|
|
|
|
|
+ // LambdaQueryWrapper<LifeDiscountCouponStoreFriend> friendCouponWrapper = new LambdaQueryWrapper<>();
|
|
|
|
|
+ // friendCouponWrapper.eq(LifeDiscountCouponStoreFriend::getStoreUserId, storeUserId)
|
|
|
|
|
+ // .between(LifeDiscountCouponStoreFriend::getCreatedTime, startDate, endDate);
|
|
|
|
|
+ // TODO: 需要根据实际业务逻辑完善统计
|
|
|
|
|
+
|
|
|
|
|
+ couponData.setGiftToFriendsCount(0L);
|
|
|
|
|
+ couponData.setGiftToFriendsAmount(BigDecimal.ZERO);
|
|
|
|
|
+ couponData.setGiftToFriendsUsedCount(0L);
|
|
|
|
|
+ couponData.setGiftToFriendsUsedAmount(BigDecimal.ZERO);
|
|
|
|
|
+ couponData.setGiftToFriendsUsedAmountRatio(BigDecimal.ZERO);
|
|
|
|
|
+ couponData.setFriendsGiftCount(0L);
|
|
|
|
|
+ couponData.setFriendsGiftAmount(BigDecimal.ZERO);
|
|
|
|
|
+ couponData.setFriendsGiftUsedCount(0L);
|
|
|
|
|
+ couponData.setFriendsGiftUsedAmount(BigDecimal.ZERO);
|
|
|
|
|
+ couponData.setFriendsGiftUsedAmountRatio(BigDecimal.ZERO);
|
|
|
|
|
+
|
|
|
|
|
+ return couponData;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取代金券数据(与优惠券类似,根据实际业务区分)
|
|
|
|
|
+ */
|
|
|
|
|
+ private StoreOperationalStatisticsVo.VoucherData getVoucherData(Integer storeId, Date startDate, Date endDate) {
|
|
|
|
|
+ StoreOperationalStatisticsVo.VoucherData voucherData = new StoreOperationalStatisticsVo.VoucherData();
|
|
|
|
|
+
|
|
|
|
|
+ // TODO: 实现代金券相关统计(与优惠券逻辑类似)
|
|
|
|
|
+ voucherData.setGiftToFriendsCount(0L);
|
|
|
|
|
+ voucherData.setGiftToFriendsAmount(BigDecimal.ZERO);
|
|
|
|
|
+ voucherData.setGiftToFriendsUsedCount(0L);
|
|
|
|
|
+ voucherData.setGiftToFriendsUsedAmount(BigDecimal.ZERO);
|
|
|
|
|
+ voucherData.setGiftToFriendsUsedAmountRatio(BigDecimal.ZERO);
|
|
|
|
|
+ voucherData.setFriendsGiftCount(0L);
|
|
|
|
|
+ voucherData.setFriendsGiftAmount(BigDecimal.ZERO);
|
|
|
|
|
+ voucherData.setFriendsGiftUsedCount(0L);
|
|
|
|
|
+ voucherData.setFriendsGiftUsedAmount(BigDecimal.ZERO);
|
|
|
|
|
+ voucherData.setFriendsGiftUsedAmountRatio(BigDecimal.ZERO);
|
|
|
|
|
+
|
|
|
|
|
+ return voucherData;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取服务质量数据
|
|
|
|
|
+ */
|
|
|
|
|
+ private StoreOperationalStatisticsVo.ServiceQualityData getServiceQualityData(Integer storeId, Date startDate, Date endDate) {
|
|
|
|
|
+ StoreOperationalStatisticsVo.ServiceQualityData serviceQualityData = new StoreOperationalStatisticsVo.ServiceQualityData();
|
|
|
|
|
+
|
|
|
|
|
+ // 查询评价数据(StoreEvaluation)
|
|
|
|
|
+ LambdaQueryWrapper<StoreEvaluation> evaluationWrapper = new LambdaQueryWrapper<>();
|
|
|
|
|
+ evaluationWrapper.eq(StoreEvaluation::getStoreId, storeId)
|
|
|
|
|
+ .between(StoreEvaluation::getCreatedTime, startDate, endDate);
|
|
|
|
|
+ List<StoreEvaluation> evaluations = storeEvaluationMapper.selectList(evaluationWrapper);
|
|
|
|
|
+
|
|
|
|
|
+ if (!evaluations.isEmpty()) {
|
|
|
|
|
+ // 计算平均评分
|
|
|
|
|
+ double avgScore = evaluations.stream()
|
|
|
|
|
+ .mapToDouble(e -> e.getScore() != null ? e.getScore().doubleValue() : 0.0)
|
|
|
|
|
+ .average()
|
|
|
|
|
+ .orElse(0.0);
|
|
|
|
|
+ serviceQualityData.setStoreRating(BigDecimal.valueOf(avgScore).setScale(1, RoundingMode.HALF_UP));
|
|
|
|
|
+
|
|
|
|
|
+ // TODO: 口味评分、环境评分、服务评分需要根据评价详情表查询
|
|
|
|
|
+ serviceQualityData.setTasteRating(BigDecimal.ZERO);
|
|
|
|
|
+ serviceQualityData.setEnvironmentRating(BigDecimal.ZERO);
|
|
|
|
|
+ serviceQualityData.setServiceRating(BigDecimal.ZERO);
|
|
|
|
|
+
|
|
|
|
|
+ // 评价数量
|
|
|
|
|
+ serviceQualityData.setTotalReviews((long) evaluations.size());
|
|
|
|
|
+
|
|
|
|
|
+ // 好评、中评、差评统计(StoreEvaluation.score 是 Double 类型)
|
|
|
|
|
+ long positiveCount = evaluations.stream()
|
|
|
|
|
+ .filter(e -> e.getScore() != null && e.getScore() >= 4.5)
|
|
|
|
|
+ .count();
|
|
|
|
|
+ long neutralCount = evaluations.stream()
|
|
|
|
|
+ .filter(e -> e.getScore() != null && e.getScore() >= 3.5 && e.getScore() < 4.5)
|
|
|
|
|
+ .count();
|
|
|
|
|
+ long negativeCount = evaluations.stream()
|
|
|
|
|
+ .filter(e -> e.getScore() != null && e.getScore() < 3.5)
|
|
|
|
|
+ .count();
|
|
|
|
|
+
|
|
|
|
|
+ serviceQualityData.setPositiveReviews(positiveCount);
|
|
|
|
|
+ serviceQualityData.setNeutralReviews(neutralCount);
|
|
|
|
|
+ serviceQualityData.setNegativeReviews(negativeCount);
|
|
|
|
|
+
|
|
|
|
|
+ // 差评占比
|
|
|
|
|
+ if (evaluations.size() > 0) {
|
|
|
|
|
+ BigDecimal ratio = BigDecimal.valueOf(negativeCount)
|
|
|
|
|
+ .divide(BigDecimal.valueOf(evaluations.size()), 4, RoundingMode.HALF_UP)
|
|
|
|
|
+ .multiply(BigDecimal.valueOf(100));
|
|
|
|
|
+ serviceQualityData.setNegativeReviewRatio(ratio.setScale(2, RoundingMode.HALF_UP));
|
|
|
|
|
+ } else {
|
|
|
|
|
+ serviceQualityData.setNegativeReviewRatio(BigDecimal.ZERO);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 差评申诉相关统计
|
|
|
|
|
+ // TODO: 需要查询差评申诉表 StoreCommentAppeal
|
|
|
|
|
+ serviceQualityData.setNegativeReviewAppealsCount(0L);
|
|
|
|
|
+ serviceQualityData.setNegativeReviewAppealsSuccessCount(0L);
|
|
|
|
|
+ serviceQualityData.setNegativeReviewAppealsSuccessRatio(BigDecimal.ZERO);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ serviceQualityData.setStoreRating(BigDecimal.ZERO);
|
|
|
|
|
+ serviceQualityData.setTasteRating(BigDecimal.ZERO);
|
|
|
|
|
+ serviceQualityData.setEnvironmentRating(BigDecimal.ZERO);
|
|
|
|
|
+ serviceQualityData.setServiceRating(BigDecimal.ZERO);
|
|
|
|
|
+ serviceQualityData.setTotalReviews(0L);
|
|
|
|
|
+ serviceQualityData.setPositiveReviews(0L);
|
|
|
|
|
+ serviceQualityData.setNeutralReviews(0L);
|
|
|
|
|
+ serviceQualityData.setNegativeReviews(0L);
|
|
|
|
|
+ serviceQualityData.setNegativeReviewRatio(BigDecimal.ZERO);
|
|
|
|
|
+ serviceQualityData.setNegativeReviewAppealsCount(0L);
|
|
|
|
|
+ serviceQualityData.setNegativeReviewAppealsSuccessCount(0L);
|
|
|
|
|
+ serviceQualityData.setNegativeReviewAppealsSuccessRatio(BigDecimal.ZERO);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return serviceQualityData;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取价目表排名数据
|
|
|
|
|
+ */
|
|
|
|
|
+ private List<StoreOperationalStatisticsVo.PriceListRanking> getPriceListRanking(Integer storeId, Date startDate, Date endDate) {
|
|
|
|
|
+ List<StoreOperationalStatisticsVo.PriceListRanking> rankings = new ArrayList<>();
|
|
|
|
|
+
|
|
|
|
|
+ // TODO: 实现价目表排名统计
|
|
|
|
|
+ // 需要查询价目表(StorePrice)的浏览量、访客数、分享数等
|
|
|
|
|
+
|
|
|
|
|
+ LambdaQueryWrapper<StorePrice> priceWrapper = new LambdaQueryWrapper<>();
|
|
|
|
|
+ priceWrapper.eq(StorePrice::getStoreId, storeId);
|
|
|
|
|
+ List<StorePrice> prices = storePriceMapper.selectList(priceWrapper);
|
|
|
|
|
+
|
|
|
|
|
+ // TODO: 需要根据浏览记录统计每个价目表的浏览量和访客数
|
|
|
|
|
+ // 示例逻辑(需要根据实际业务完善)
|
|
|
|
|
+ int rank = 1;
|
|
|
|
|
+ for (StorePrice price : prices) {
|
|
|
|
|
+ StoreOperationalStatisticsVo.PriceListRanking ranking = new StoreOperationalStatisticsVo.PriceListRanking();
|
|
|
|
|
+ ranking.setRank(rank++);
|
|
|
|
|
+ ranking.setPriceListItemName(price.getName());
|
|
|
|
|
+ // TODO: 统计该价目表的浏览量、访客数、分享数
|
|
|
|
|
+ ranking.setPageViews(0L);
|
|
|
|
|
+ ranking.setVisitors(0L);
|
|
|
|
|
+ ranking.setShares(0L);
|
|
|
|
|
+ rankings.add(ranking);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return rankings;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 构建流量数据对比
|
|
|
|
|
+ */
|
|
|
|
|
+ private StoreOperationalStatisticsComparisonVo.TrafficDataComparison buildTrafficDataComparison(
|
|
|
|
|
+ StoreOperationalStatisticsVo.TrafficData current, StoreOperationalStatisticsVo.TrafficData previous) {
|
|
|
|
|
+ StoreOperationalStatisticsComparisonVo.TrafficDataComparison comparison = new StoreOperationalStatisticsComparisonVo.TrafficDataComparison();
|
|
|
|
|
+ comparison.setStoreSearchVolume(buildComparisonData(current.getStoreSearchVolume(), previous.getStoreSearchVolume()));
|
|
|
|
|
+ comparison.setPageViews(buildComparisonData(current.getPageViews(), previous.getPageViews()));
|
|
|
|
|
+ comparison.setVisitors(buildComparisonData(current.getVisitors(), previous.getVisitors()));
|
|
|
|
|
+ comparison.setNewVisitors(buildComparisonData(current.getNewVisitors(), previous.getNewVisitors()));
|
|
|
|
|
+ comparison.setVisitDuration(buildComparisonData(current.getVisitDuration(), previous.getVisitDuration()));
|
|
|
|
|
+ comparison.setAvgVisitDuration(buildComparisonData(current.getAvgVisitDuration(), previous.getAvgVisitDuration()));
|
|
|
|
|
+ return comparison;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 构建互动数据对比
|
|
|
|
|
+ */
|
|
|
|
|
+ private StoreOperationalStatisticsComparisonVo.InteractionDataComparison buildInteractionDataComparison(
|
|
|
|
|
+ StoreOperationalStatisticsVo.InteractionData current, StoreOperationalStatisticsVo.InteractionData previous) {
|
|
|
|
|
+ StoreOperationalStatisticsComparisonVo.InteractionDataComparison comparison = new StoreOperationalStatisticsComparisonVo.InteractionDataComparison();
|
|
|
|
|
+ comparison.setStoreCollectionCount(buildComparisonData(current.getStoreCollectionCount(), previous.getStoreCollectionCount()));
|
|
|
|
|
+ comparison.setStoreShareCount(buildComparisonData(current.getStoreShareCount(), previous.getStoreShareCount()));
|
|
|
|
|
+ comparison.setStoreCheckInCount(buildComparisonData(current.getStoreCheckInCount(), previous.getStoreCheckInCount()));
|
|
|
|
|
+ comparison.setConsultMerchantCount(buildComparisonData(current.getConsultMerchantCount(), previous.getConsultMerchantCount()));
|
|
|
|
|
+ comparison.setFriendsCount(buildComparisonData(current.getFriendsCount(), previous.getFriendsCount()));
|
|
|
|
|
+ comparison.setFollowCount(buildComparisonData(current.getFollowCount(), previous.getFollowCount()));
|
|
|
|
|
+ comparison.setFansCount(buildComparisonData(current.getFansCount(), previous.getFansCount()));
|
|
|
|
|
+ comparison.setPostsPublishedCount(buildComparisonData(current.getPostsPublishedCount(), previous.getPostsPublishedCount()));
|
|
|
|
|
+ comparison.setPostLikesCount(buildComparisonData(current.getPostLikesCount(), previous.getPostLikesCount()));
|
|
|
|
|
+ comparison.setPostCommentsCount(buildComparisonData(current.getPostCommentsCount(), previous.getPostCommentsCount()));
|
|
|
|
|
+ comparison.setPostSharesCount(buildComparisonData(current.getPostSharesCount(), previous.getPostSharesCount()));
|
|
|
|
|
+ comparison.setReportedCount(buildComparisonData(current.getReportedCount(), previous.getReportedCount()));
|
|
|
|
|
+ comparison.setBlockedCount(buildComparisonData(current.getBlockedCount(), previous.getBlockedCount()));
|
|
|
|
|
+ return comparison;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 构建优惠券数据对比
|
|
|
|
|
+ */
|
|
|
|
|
+ private StoreOperationalStatisticsComparisonVo.CouponDataComparison buildCouponDataComparison(
|
|
|
|
|
+ StoreOperationalStatisticsVo.CouponData current, StoreOperationalStatisticsVo.CouponData previous) {
|
|
|
|
|
+ StoreOperationalStatisticsComparisonVo.CouponDataComparison comparison = new StoreOperationalStatisticsComparisonVo.CouponDataComparison();
|
|
|
|
|
+ comparison.setGiftToFriendsCount(buildComparisonData(current.getGiftToFriendsCount(), previous.getGiftToFriendsCount()));
|
|
|
|
|
+ comparison.setGiftToFriendsAmount(buildComparisonData(current.getGiftToFriendsAmount(), previous.getGiftToFriendsAmount()));
|
|
|
|
|
+ comparison.setGiftToFriendsUsedCount(buildComparisonData(current.getGiftToFriendsUsedCount(), previous.getGiftToFriendsUsedCount()));
|
|
|
|
|
+ comparison.setGiftToFriendsUsedAmount(buildComparisonData(current.getGiftToFriendsUsedAmount(), previous.getGiftToFriendsUsedAmount()));
|
|
|
|
|
+ comparison.setGiftToFriendsUsedAmountRatio(buildComparisonData(current.getGiftToFriendsUsedAmountRatio(), previous.getGiftToFriendsUsedAmountRatio()));
|
|
|
|
|
+ comparison.setFriendsGiftCount(buildComparisonData(current.getFriendsGiftCount(), previous.getFriendsGiftCount()));
|
|
|
|
|
+ comparison.setFriendsGiftAmount(buildComparisonData(current.getFriendsGiftAmount(), previous.getFriendsGiftAmount()));
|
|
|
|
|
+ comparison.setFriendsGiftUsedCount(buildComparisonData(current.getFriendsGiftUsedCount(), previous.getFriendsGiftUsedCount()));
|
|
|
|
|
+ comparison.setFriendsGiftUsedAmount(buildComparisonData(current.getFriendsGiftUsedAmount(), previous.getFriendsGiftUsedAmount()));
|
|
|
|
|
+ comparison.setFriendsGiftUsedAmountRatio(buildComparisonData(current.getFriendsGiftUsedAmountRatio(), previous.getFriendsGiftUsedAmountRatio()));
|
|
|
|
|
+ return comparison;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 构建代金券数据对比
|
|
|
|
|
+ */
|
|
|
|
|
+ private StoreOperationalStatisticsComparisonVo.VoucherDataComparison buildVoucherDataComparison(
|
|
|
|
|
+ StoreOperationalStatisticsVo.VoucherData current, StoreOperationalStatisticsVo.VoucherData previous) {
|
|
|
|
|
+ StoreOperationalStatisticsComparisonVo.VoucherDataComparison comparison = new StoreOperationalStatisticsComparisonVo.VoucherDataComparison();
|
|
|
|
|
+ comparison.setGiftToFriendsCount(buildComparisonData(current.getGiftToFriendsCount(), previous.getGiftToFriendsCount()));
|
|
|
|
|
+ comparison.setGiftToFriendsAmount(buildComparisonData(current.getGiftToFriendsAmount(), previous.getGiftToFriendsAmount()));
|
|
|
|
|
+ comparison.setGiftToFriendsUsedCount(buildComparisonData(current.getGiftToFriendsUsedCount(), previous.getGiftToFriendsUsedCount()));
|
|
|
|
|
+ comparison.setGiftToFriendsUsedAmount(buildComparisonData(current.getGiftToFriendsUsedAmount(), previous.getGiftToFriendsUsedAmount()));
|
|
|
|
|
+ comparison.setGiftToFriendsUsedAmountRatio(buildComparisonData(current.getGiftToFriendsUsedAmountRatio(), previous.getGiftToFriendsUsedAmountRatio()));
|
|
|
|
|
+ comparison.setFriendsGiftCount(buildComparisonData(current.getFriendsGiftCount(), previous.getFriendsGiftCount()));
|
|
|
|
|
+ comparison.setFriendsGiftAmount(buildComparisonData(current.getFriendsGiftAmount(), previous.getFriendsGiftAmount()));
|
|
|
|
|
+ comparison.setFriendsGiftUsedCount(buildComparisonData(current.getFriendsGiftUsedCount(), previous.getFriendsGiftUsedCount()));
|
|
|
|
|
+ comparison.setFriendsGiftUsedAmount(buildComparisonData(current.getFriendsGiftUsedAmount(), previous.getFriendsGiftUsedAmount()));
|
|
|
|
|
+ comparison.setFriendsGiftUsedAmountRatio(buildComparisonData(current.getFriendsGiftUsedAmountRatio(), previous.getFriendsGiftUsedAmountRatio()));
|
|
|
|
|
+ return comparison;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 构建服务质量数据对比
|
|
|
|
|
+ */
|
|
|
|
|
+ private StoreOperationalStatisticsComparisonVo.ServiceQualityDataComparison buildServiceQualityDataComparison(
|
|
|
|
|
+ StoreOperationalStatisticsVo.ServiceQualityData current, StoreOperationalStatisticsVo.ServiceQualityData previous) {
|
|
|
|
|
+ StoreOperationalStatisticsComparisonVo.ServiceQualityDataComparison comparison = new StoreOperationalStatisticsComparisonVo.ServiceQualityDataComparison();
|
|
|
|
|
+ comparison.setStoreRating(buildComparisonData(current.getStoreRating(), previous.getStoreRating()));
|
|
|
|
|
+ comparison.setTasteRating(buildComparisonData(current.getTasteRating(), previous.getTasteRating()));
|
|
|
|
|
+ comparison.setEnvironmentRating(buildComparisonData(current.getEnvironmentRating(), previous.getEnvironmentRating()));
|
|
|
|
|
+ comparison.setServiceRating(buildComparisonData(current.getServiceRating(), previous.getServiceRating()));
|
|
|
|
|
+ comparison.setTotalReviews(buildComparisonData(current.getTotalReviews(), previous.getTotalReviews()));
|
|
|
|
|
+ comparison.setPositiveReviews(buildComparisonData(current.getPositiveReviews(), previous.getPositiveReviews()));
|
|
|
|
|
+ comparison.setNeutralReviews(buildComparisonData(current.getNeutralReviews(), previous.getNeutralReviews()));
|
|
|
|
|
+ comparison.setNegativeReviews(buildComparisonData(current.getNegativeReviews(), previous.getNegativeReviews()));
|
|
|
|
|
+ comparison.setNegativeReviewRatio(buildComparisonData(current.getNegativeReviewRatio(), previous.getNegativeReviewRatio()));
|
|
|
|
|
+ comparison.setNegativeReviewAppealsCount(buildComparisonData(current.getNegativeReviewAppealsCount(), previous.getNegativeReviewAppealsCount()));
|
|
|
|
|
+ comparison.setNegativeReviewAppealsSuccessCount(buildComparisonData(current.getNegativeReviewAppealsSuccessCount(), previous.getNegativeReviewAppealsSuccessCount()));
|
|
|
|
|
+ comparison.setNegativeReviewAppealsSuccessRatio(buildComparisonData(current.getNegativeReviewAppealsSuccessRatio(), previous.getNegativeReviewAppealsSuccessRatio()));
|
|
|
|
|
+ return comparison;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 构建对比数据
|
|
|
|
|
+ */
|
|
|
|
|
+ private StoreOperationalStatisticsComparisonVo.BaseComparisonData buildComparisonData(Object current, Object previous) {
|
|
|
|
|
+ StoreOperationalStatisticsComparisonVo.BaseComparisonData comparisonData = new StoreOperationalStatisticsComparisonVo.BaseComparisonData();
|
|
|
|
|
+ comparisonData.setCurrent(current);
|
|
|
|
|
+ comparisonData.setPrevious(previous);
|
|
|
|
|
+
|
|
|
|
|
+ // 计算变化率
|
|
|
|
|
+ BigDecimal changeRate = calculateChangeRate(current, previous);
|
|
|
|
|
+ comparisonData.setChangeRate(changeRate);
|
|
|
|
|
+
|
|
|
|
|
+ return comparisonData;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 计算变化率(百分比)
|
|
|
|
|
+ */
|
|
|
|
|
+ private BigDecimal calculateChangeRate(Object current, Object previous) {
|
|
|
|
|
+ BigDecimal currentValue = toBigDecimal(current);
|
|
|
|
|
+ BigDecimal previousValue = toBigDecimal(previous);
|
|
|
|
|
+
|
|
|
|
|
+ if (previousValue == null || previousValue.compareTo(BigDecimal.ZERO) == 0) {
|
|
|
|
|
+ if (currentValue != null && currentValue.compareTo(BigDecimal.ZERO) > 0) {
|
|
|
|
|
+ return BigDecimal.valueOf(100); // 从0增长,视为100%增长
|
|
|
|
|
+ }
|
|
|
|
|
+ return BigDecimal.ZERO;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (currentValue == null) {
|
|
|
|
|
+ currentValue = BigDecimal.ZERO;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 变化率 = ((当期 - 上期) / 上期) * 100
|
|
|
|
|
+ BigDecimal change = currentValue.subtract(previousValue);
|
|
|
|
|
+ BigDecimal rate = change.divide(previousValue, 4, RoundingMode.HALF_UP)
|
|
|
|
|
+ .multiply(BigDecimal.valueOf(100));
|
|
|
|
|
+ return rate.setScale(2, RoundingMode.HALF_UP);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 将对象转换为BigDecimal
|
|
|
|
|
+ */
|
|
|
|
|
+ private BigDecimal toBigDecimal(Object value) {
|
|
|
|
|
+ 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;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|