|
@@ -0,0 +1,340 @@
|
|
|
+package com.fs.live.service.impl;
|
|
|
+
|
|
|
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
|
+import com.fs.common.core.redis.RedisCache;
|
|
|
+import com.fs.common.utils.DateUtils;
|
|
|
+import com.fs.common.utils.spring.SpringUtils;
|
|
|
+import com.fs.live.domain.LiveData;
|
|
|
+import com.fs.live.mapper.LiveDataMapper;
|
|
|
+import com.fs.live.service.ILiveDataService;
|
|
|
+import com.fs.live.vo.ColumnsConfigVo;
|
|
|
+import com.fs.live.vo.DateRange;
|
|
|
+import com.fs.live.vo.RecentLiveDataVo;
|
|
|
+import com.fs.live.vo.TrendDataVO;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+
|
|
|
+import java.math.BigDecimal;
|
|
|
+import java.math.RoundingMode;
|
|
|
+import java.time.DayOfWeek;
|
|
|
+import java.time.LocalDate;
|
|
|
+import java.time.format.DateTimeFormatter;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 直播数据Service业务层处理
|
|
|
+ *
|
|
|
+ * @author fs
|
|
|
+ * @date 2025-03-05
|
|
|
+ */
|
|
|
+@Service
|
|
|
+public class LiveDataServiceImpl extends ServiceImpl<LiveDataMapper, LiveData> implements ILiveDataService {
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private LiveDataMapper liveDataMapper;
|
|
|
+ private final RedisCache redisCache = SpringUtils.getBean(RedisCache.class);
|
|
|
+ private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
|
|
+ private final String COLUMNS_CONFIG = "columnsConfigKey";
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 查询直播数据
|
|
|
+ *
|
|
|
+ * @param liveId 直播数据主键
|
|
|
+ * @return 直播数据
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public LiveData selectLiveDataByLiveId(Long liveId)
|
|
|
+ {
|
|
|
+ return baseMapper.selectLiveDataByLiveId(liveId);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 查询直播数据列表
|
|
|
+ *
|
|
|
+ * @param liveData 直播数据
|
|
|
+ * @return 直播数据
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public List<LiveData> selectLiveDataList(LiveData liveData)
|
|
|
+ {
|
|
|
+ return baseMapper.selectLiveDataList(liveData);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 新增直播数据
|
|
|
+ *
|
|
|
+ * @param liveData 直播数据
|
|
|
+ * @return 结果
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public int insertLiveData(LiveData liveData)
|
|
|
+ {
|
|
|
+ liveData.setCreateTime(DateUtils.getNowDate());
|
|
|
+ return baseMapper.insertLiveData(liveData);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 修改直播数据
|
|
|
+ *
|
|
|
+ * @param liveData 直播数据
|
|
|
+ * @return 结果
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public int updateLiveData(LiveData liveData)
|
|
|
+ {
|
|
|
+ return baseMapper.updateLiveData(liveData);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 批量删除直播数据
|
|
|
+ *
|
|
|
+ * @param liveIds 需要删除的直播数据主键
|
|
|
+ * @return 结果
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public int deleteLiveDataByLiveIds(Long[] liveIds)
|
|
|
+ {
|
|
|
+ return baseMapper.deleteLiveDataByLiveIds(liveIds);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 删除直播数据信息
|
|
|
+ *
|
|
|
+ * @param liveId 直播数据主键
|
|
|
+ * @return 结果
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public int deleteLiveDataByLiveId(Long liveId)
|
|
|
+ {
|
|
|
+ return baseMapper.deleteLiveDataByLiveId(liveId);
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 查询所有正在直播的直播间iD
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public List<Long> getAllLiveIds() {
|
|
|
+ return baseMapper.getAllLiveIds();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<RecentLiveDataVo> getRecentLive() {
|
|
|
+ return liveDataMapper.getRecentLive();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<RecentLiveDataVo> getLiveTop(String rankType) {
|
|
|
+ return liveDataMapper.getLiveTop(rankType);
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 查询直播趋势数据
|
|
|
+ * @param type
|
|
|
+ * @param selectedDate
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public TrendDataVO getTrendData(String type, String selectedDate,String category) {
|
|
|
+ //DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
|
|
+ LocalDate date = LocalDate.parse(selectedDate, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
|
|
|
+ DateRange currentRange = getDateRange(type, date, 0);
|
|
|
+ DateRange previousRange = getDateRange(type, date, -1);
|
|
|
+ DateRange chartRange = getChartDateRange(type, date); // 天 / 10 周 / 12 月
|
|
|
+ // 查询当前周期数据
|
|
|
+ TrendDataVO currentData = liveDataMapper.getCurrentData(currentRange.getStart(), currentRange.getEnd());
|
|
|
+ if (currentData == null) {
|
|
|
+ currentData = new TrendDataVO();
|
|
|
+ }
|
|
|
+ // 查询上一周期数据
|
|
|
+ TrendDataVO prevData = liveDataMapper.getPreviousData(previousRange.getStart(), previousRange.getEnd());
|
|
|
+ if (prevData == null) {
|
|
|
+ prevData = new TrendDataVO();
|
|
|
+ }
|
|
|
+ List<Map<String, Object>> chartRawData = liveDataMapper.getChartData(chartRange.getStart(), chartRange.getEnd(), type,category.trim());
|
|
|
+ List<String> dates = new ArrayList<>();
|
|
|
+ List<Integer> data = new ArrayList<>();
|
|
|
+
|
|
|
+ for (Map<String, Object> row : chartRawData) {
|
|
|
+ String dateRange = (String) row.get("date_range");
|
|
|
+ Integer views = ((Number) row.get("views")).intValue();
|
|
|
+
|
|
|
+ dates.add(dateRange);
|
|
|
+ data.add(views);
|
|
|
+ }
|
|
|
+
|
|
|
+ currentData.setDates(dates);
|
|
|
+ currentData.setData(data);
|
|
|
+ //计算和上一个周期相比的变化百分比
|
|
|
+ currentData.setViewsChange(calcChange(
|
|
|
+ currentData.getViews() != null ? currentData.getViews() : 0,
|
|
|
+ prevData.getPrevViews() != null ? prevData.getPrevViews() : 0));
|
|
|
+
|
|
|
+ currentData.setVisitorsChange(calcChange(
|
|
|
+ currentData.getVisitors() != null ? currentData.getVisitors() : 0,
|
|
|
+ prevData.getPrevVisitors() != null ? prevData.getPrevVisitors() : 0));
|
|
|
+
|
|
|
+ currentData.setStreamsChange(calcChange(
|
|
|
+ currentData.getStreams() != null ? currentData.getStreams() : 0,
|
|
|
+ prevData.getPrevStreams() != null ? prevData.getPrevStreams() : 0));
|
|
|
+
|
|
|
+ currentData.setPvChange(calcChange(
|
|
|
+ currentData.getPv() != null ? currentData.getPv() : 0,
|
|
|
+ prevData.getPrevPv() != null ? prevData.getPrevPv() : 0));
|
|
|
+
|
|
|
+ currentData.setUvChange(calcChange(
|
|
|
+ currentData.getUv() != null ? currentData.getUv() : 0,
|
|
|
+ prevData.getPrevUv() != null ? prevData.getPrevUv() : 0));
|
|
|
+
|
|
|
+ return currentData;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ *
|
|
|
+ * @param current 当前周期
|
|
|
+ * @param previous 上一周期
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ private double calcChange(Integer current, Integer previous) {
|
|
|
+ if (current==previous){return 0.0;}
|
|
|
+ if (previous == null || previous == 0) {return 100.0;}
|
|
|
+ double change = ((double) (current - previous) / previous) * 100;
|
|
|
+ BigDecimal bd = new BigDecimal(change).setScale(2, RoundingMode.HALF_UP);
|
|
|
+ return bd.doubleValue();
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 获取当前/上个周期的时间范围
|
|
|
+ */
|
|
|
+ private DateRange getDateRange(String type, LocalDate date, int offset) {
|
|
|
+ LocalDate start, end;
|
|
|
+ switch (type) {
|
|
|
+ case "week":
|
|
|
+ start = date.minusWeeks(-offset).with(DayOfWeek.MONDAY); // 该周的周一
|
|
|
+ end = start.plusDays(6); // 该周的周日
|
|
|
+ break;
|
|
|
+ case "month":
|
|
|
+ start = date.minusMonths(-offset).withDayOfMonth(1); // 该月第一天
|
|
|
+ end = start.plusMonths(1).minusDays(1); // 该月最后一天
|
|
|
+ break;
|
|
|
+ default: // "day"
|
|
|
+ start = date.minusDays(Math.abs(offset)); // 计算 start
|
|
|
+ end = date;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return new DateRange(start.toString(), end.toString());
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取折线图查询范围
|
|
|
+ */
|
|
|
+ private DateRange getChartDateRange(String type, LocalDate date) {
|
|
|
+ LocalDate start, end;
|
|
|
+ switch (type) {
|
|
|
+ case "week":
|
|
|
+ start = date.minusWeeks(9).with(DayOfWeek.MONDAY);
|
|
|
+ end = date.with(DayOfWeek.SUNDAY);
|
|
|
+ break;
|
|
|
+ case "month":
|
|
|
+ start = date.minusMonths(11).withDayOfMonth(1);
|
|
|
+ end = date.withDayOfMonth(date.lengthOfMonth());
|
|
|
+ break;
|
|
|
+ default: // day
|
|
|
+ start = date.minusDays(30);
|
|
|
+ end = date;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return new DateRange(start.format(FORMATTER), end.format(FORMATTER));
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * @param userId 当前登录用户id
|
|
|
+ * 查询表格字段配置
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ /*@Override
|
|
|
+ public List<ColumnsConfigVo> getColumnsConfig(String userId) {
|
|
|
+ List<Object> cacheList = redisCache.getCacheObject(COLUMNS_CONFIG + userId + ":");
|
|
|
+ if (cacheList == null){
|
|
|
+ List<ColumnsConfigVo> columns = new ArrayList<>();
|
|
|
+ *//*columns.add(new ColumnsConfigVo("customer_name", "客户名", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("live_room", "直播间", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("follower", "跟进人", "ENABLE"));*//*
|
|
|
+ columns.add(new ColumnsConfigVo("department", "归属部门", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("created_time", "客户创建时间", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("contact_method", "联系方式", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("order_goods", "去下单实物商品", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("share_live_count", "分享直播间次数", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("reply_count", "答题次数", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("correct_reply_count", "答题正确次数", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("reply_accuracy", "答题正确率", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("first_visit_time", "首次访问时间", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("first_visit_type", "首次访问类型", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("max_live_stay_time", "直播最长停留时间", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("total_live_watch_time", "直播累计观看时长", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("max_replay_stay_time", "回放最长停留时间", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("total_replay_watch_time", "回放累计观看时长", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("total_watch_time", "总累计观看时长", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("last_exit_time", "最后一次退出时间", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("speak_count", "发言次数", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("visit_form_name", "访问表单名称", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("submit_form_name", "提交表单名称", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("pending_payment_goods", "待支付商品", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("purchased_goods", "购买商品名称", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("last_purchase_time", "最近商品购买时间", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("spending_amount", "消费金额", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("coupon_name", "优惠券名称", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("is_participate_lottery", "是否参与抽奖", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("is_winner", "是否中奖", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("claimed_watch_reward", "领取观看奖励", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("reward_withdrawal_amount", "奖励提现金额", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("reward_type", "奖励类型", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("red_envelope_count", "红包领取次数", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("click_external_goods", "点击外链商品次数", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("is_request_mic", "是否申请连麦", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("is_on_mic", "是否连麦", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("advertiser", "广告商", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("promotion_account", "推广账户", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("promotion_level_1", "推广一级", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("promotion_level_2", "推广二级", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("site_placement", "站点投放", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("channel", "渠道", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("tags", "标签", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("follow_status", "跟进状态", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("sharer_name", "分享人名称", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("customer_id", "客户ID", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("union_id", "unionId", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("source", "来源", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("landing_page", "着陆页", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("live_room_url", "直播间访问地址", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("collaborator", "协作人", "ENABLE"));
|
|
|
+ columns.add(new ColumnsConfigVo("wechat_note", "微信备注", "ENABLE"));
|
|
|
+ redisCache.setCacheList(COLUMNS_CONFIG + userId + ":",columns);
|
|
|
+ return columns;
|
|
|
+ }
|
|
|
+ // 如果缓存中有数据,将其转换为 ColumnsConfigVo 对象并返回
|
|
|
+ List<ColumnsConfigVo> columnsConfigList = new ArrayList<>();
|
|
|
+ for (Object obj : cacheList) {
|
|
|
+ if (obj instanceof ColumnsConfigVo) {
|
|
|
+ columnsConfigList.add((ColumnsConfigVo) obj);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return columnsConfigList;
|
|
|
+ }
|
|
|
+*/
|
|
|
+ @Override
|
|
|
+ public void saveColumnsConfig(String userId, List<ColumnsConfigVo> columns) {
|
|
|
+ redisCache.deleteObject(COLUMNS_CONFIG + userId + ":");
|
|
|
+ redisCache.setCacheObject(COLUMNS_CONFIG + userId + ":", columns);
|
|
|
+ }
|
|
|
+ private Map<String, String> createColumn(String dataIndex, String status, String title) {
|
|
|
+ Map<String, String> column = new HashMap<>();
|
|
|
+ column.put("dataIndex", dataIndex);
|
|
|
+ column.put("status", status);
|
|
|
+ column.put("title", title);
|
|
|
+ return column;
|
|
|
+ }
|
|
|
+
|
|
|
+}
|