|
@@ -10,6 +10,7 @@ import com.alibaba.fastjson.JSONObject;
|
|
|
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
|
|
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
|
|
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
|
import com.fs.common.core.domain.R;
|
|
import com.fs.common.core.domain.R;
|
|
|
|
|
+import com.fs.common.core.page.TableDataInfo;
|
|
|
import com.fs.common.core.redis.RedisCache;
|
|
import com.fs.common.core.redis.RedisCache;
|
|
|
import com.fs.common.exception.base.BusinessException;
|
|
import com.fs.common.exception.base.BusinessException;
|
|
|
import com.fs.common.utils.DateUtils;
|
|
import com.fs.common.utils.DateUtils;
|
|
@@ -90,6 +91,7 @@ import java.util.*;
|
|
|
import java.util.concurrent.CompletableFuture;
|
|
import java.util.concurrent.CompletableFuture;
|
|
|
import java.util.concurrent.ExecutorService;
|
|
import java.util.concurrent.ExecutorService;
|
|
|
import java.util.concurrent.Executors;
|
|
import java.util.concurrent.Executors;
|
|
|
|
|
+import java.util.concurrent.TimeUnit;
|
|
|
import java.util.function.Function;
|
|
import java.util.function.Function;
|
|
|
import java.util.stream.Collectors;
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
|
@@ -2993,4 +2995,87 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * APP完课统计报表(含Redis缓存 + 手动分页)
|
|
|
|
|
+ * 缓存策略:
|
|
|
|
|
+ * - 查询今天的:缓存10分钟
|
|
|
|
|
+ * - 查询非今天的:缓存到当天24点失效
|
|
|
|
|
+ * 缓存的是全量数据(无分页),每次请求从缓存中取全量再分页返回
|
|
|
|
|
+ */
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public TableDataInfo selectCourseFinishAppReport(CourseFinishAppReportParam param) {
|
|
|
|
|
+ String cacheKey = buildCourseFinishAppReportCacheKey(param);
|
|
|
|
|
+
|
|
|
|
|
+ // 从缓存获取全量数据
|
|
|
|
|
+ List<CourseFinishAppReportVO> allList = redisCache.getCacheObject(cacheKey);
|
|
|
|
|
+ if (allList == null) {
|
|
|
|
|
+ // 缓存未命中,查询数据库
|
|
|
|
|
+ allList = fsCourseWatchLogMapper.selectCourseFinishAppReport(param);
|
|
|
|
|
+
|
|
|
|
|
+ // 写入缓存
|
|
|
|
|
+ String today = DateUtils.dateTimeNow("yyyy-MM-dd");
|
|
|
|
|
+ boolean isToday = today.equals(param.getStartDate()) && today.equals(param.getEndDate());
|
|
|
|
|
+ if (isToday) {
|
|
|
|
|
+ redisCache.setCacheObject(cacheKey, allList, 10, TimeUnit.MINUTES);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ long secondsUntilMidnight = getSecondsUntilMidnight();
|
|
|
|
|
+ redisCache.setCacheObject(cacheKey, allList, (int) secondsUntilMidnight, TimeUnit.SECONDS);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 手动分页(pageNum 和 pageSize 均为 null 时返回全量,用于导出)
|
|
|
|
|
+ boolean needPage = param.getPageNum() != null && param.getPageSize() != null;
|
|
|
|
|
+ int total = allList.size();
|
|
|
|
|
+ List<CourseFinishAppReportVO> pageList;
|
|
|
|
|
+ if (needPage) {
|
|
|
|
|
+ int pageNum = param.getPageNum();
|
|
|
|
|
+ int pageSize = param.getPageSize();
|
|
|
|
|
+ int fromIndex = (pageNum - 1) * pageSize;
|
|
|
|
|
+ int toIndex = Math.min(fromIndex + pageSize, total);
|
|
|
|
|
+ if (fromIndex >= total) {
|
|
|
|
|
+ pageList = Collections.emptyList();
|
|
|
|
|
+ } else {
|
|
|
|
|
+ pageList = allList.subList(fromIndex, toIndex);
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ pageList = allList;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ TableDataInfo tableDataInfo = new TableDataInfo();
|
|
|
|
|
+ tableDataInfo.setRows(pageList);
|
|
|
|
|
+ tableDataInfo.setTotal(total);
|
|
|
|
|
+ return tableDataInfo;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 构建缓存Key
|
|
|
|
|
+ */
|
|
|
|
|
+ private String buildCourseFinishAppReportCacheKey(CourseFinishAppReportParam param) {
|
|
|
|
|
+ StringBuilder sb = new StringBuilder("course:finish:app:report:");
|
|
|
|
|
+ String dimType = (param.getDimensionType() != null && !param.getDimensionType().isEmpty())
|
|
|
|
|
+ ? param.getDimensionType() : "default";
|
|
|
|
|
+ sb.append(dimType);
|
|
|
|
|
+ sb.append(":").append(param.getStartDate());
|
|
|
|
|
+ sb.append(":").append(param.getEndDate());
|
|
|
|
|
+ if (param.getCompanyId() != null) {
|
|
|
|
|
+ sb.append(":company_").append(param.getCompanyId());
|
|
|
|
|
+ }
|
|
|
|
|
+ if (param.getProjectId() != null) {
|
|
|
|
|
+ sb.append(":project_").append(param.getProjectId());
|
|
|
|
|
+ }
|
|
|
|
|
+ return sb.toString();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 计算距离当天24点剩余的秒数
|
|
|
|
|
+ */
|
|
|
|
|
+ private long getSecondsUntilMidnight() {
|
|
|
|
|
+ Calendar cal = Calendar.getInstance();
|
|
|
|
|
+ cal.add(Calendar.DAY_OF_MONTH, 1);
|
|
|
|
|
+ cal.set(Calendar.HOUR_OF_DAY, 0);
|
|
|
|
|
+ cal.set(Calendar.MINUTE, 0);
|
|
|
|
|
+ cal.set(Calendar.SECOND, 0);
|
|
|
|
|
+ cal.set(Calendar.MILLISECOND, 0);
|
|
|
|
|
+ return (cal.getTimeInMillis() - System.currentTimeMillis()) / 1000;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|