|
@@ -8,6 +8,7 @@ import com.fs.company.cache.ICompanyUserCacheService;
|
|
|
import com.fs.company.domain.Company;
|
|
import com.fs.company.domain.Company;
|
|
|
import com.fs.company.domain.CompanyUser;
|
|
import com.fs.company.domain.CompanyUser;
|
|
|
import com.fs.course.domain.FsCourseAnswerLogs;
|
|
import com.fs.course.domain.FsCourseAnswerLogs;
|
|
|
|
|
+import com.fs.course.domain.FsCourseRedPacketLog;
|
|
|
import com.fs.course.domain.FsUserCourseVideo;
|
|
import com.fs.course.domain.FsUserCourseVideo;
|
|
|
import com.fs.course.mapper.FsCourseAnswerLogsMapper;
|
|
import com.fs.course.mapper.FsCourseAnswerLogsMapper;
|
|
|
import com.fs.course.param.FsCourseAnswerLogsParam;
|
|
import com.fs.course.param.FsCourseAnswerLogsParam;
|
|
@@ -17,14 +18,15 @@ import com.fs.course.vo.FsCourseAnswerLogsListVO;
|
|
|
import com.fs.his.domain.FsUser;
|
|
import com.fs.his.domain.FsUser;
|
|
|
import com.fs.his.service.IFsUserService;
|
|
import com.fs.his.service.IFsUserService;
|
|
|
import com.fs.store.service.cache.IFsUserCacheService;
|
|
import com.fs.store.service.cache.IFsUserCacheService;
|
|
|
|
|
+import com.github.pagehelper.PageHelper;
|
|
|
import com.hc.openapi.tool.util.StringUtils;
|
|
import com.hc.openapi.tool.util.StringUtils;
|
|
|
import lombok.RequiredArgsConstructor;
|
|
import lombok.RequiredArgsConstructor;
|
|
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.stereotype.Service;
|
|
import org.springframework.stereotype.Service;
|
|
|
|
|
|
|
|
-import java.util.Collections;
|
|
|
|
|
-import java.util.List;
|
|
|
|
|
-import java.util.Set;
|
|
|
|
|
|
|
+import java.text.SimpleDateFormat;
|
|
|
|
|
+import java.util.*;
|
|
|
import java.util.stream.Collectors;
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -34,6 +36,7 @@ import java.util.stream.Collectors;
|
|
|
* @date 2024-10-26
|
|
* @date 2024-10-26
|
|
|
*/
|
|
*/
|
|
|
@Service
|
|
@Service
|
|
|
|
|
+@Slf4j
|
|
|
@RequiredArgsConstructor
|
|
@RequiredArgsConstructor
|
|
|
public class FsCourseAnswerLogsServiceImpl implements IFsCourseAnswerLogsService
|
|
public class FsCourseAnswerLogsServiceImpl implements IFsCourseAnswerLogsService
|
|
|
{
|
|
{
|
|
@@ -201,4 +204,195 @@ public class FsCourseAnswerLogsServiceImpl implements IFsCourseAnswerLogsService
|
|
|
return fsCourseAnswerLogsMapper.selectFsCourseAnswerLogsListVONewCount(param);
|
|
return fsCourseAnswerLogsMapper.selectFsCourseAnswerLogsListVONewCount(param);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public void backupAnswerLog() {
|
|
|
|
|
+ log.info("开始执行数据迁移任务");
|
|
|
|
|
+
|
|
|
|
|
+ // 检查是否在凌晨3点之后
|
|
|
|
|
+// if (shouldStopAt3AM()) {
|
|
|
|
|
+// log.info("已到凌晨3点,停止数据迁移任务");
|
|
|
|
|
+// return;
|
|
|
|
|
+// }
|
|
|
|
|
+
|
|
|
|
|
+ // 设置查询条件:获取12月之前的数据
|
|
|
|
|
+ FsCourseAnswerLogs queryLog = createQueryCondition();
|
|
|
|
|
+
|
|
|
|
|
+ // 分页参数配置
|
|
|
|
|
+ int pageSize = 1000; // 每批次处理数量,可根据性能调整
|
|
|
|
|
+ int pageNum = 1;
|
|
|
|
|
+ int totalProcessed = 0;
|
|
|
|
|
+ boolean hasMoreData = true;
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ while (hasMoreData) {
|
|
|
|
|
+ // 检查是否到凌晨3点
|
|
|
|
|
+ /* if (shouldStopAt3AM()) {
|
|
|
|
|
+ log.info("已到凌晨3点,停止数据迁移任务,已处理{}条数据", totalProcessed);
|
|
|
|
|
+ break;
|
|
|
|
|
+ }*/
|
|
|
|
|
+
|
|
|
|
|
+ log.info("开始查询第{}批数据,查询条件:beginTime={}, endTime={}",
|
|
|
|
|
+ pageNum, queryLog.getBeginTime(), queryLog.getEndTime());
|
|
|
|
|
+
|
|
|
|
|
+ // 使用分页查询
|
|
|
|
|
+ PageHelper.startPage(pageNum, pageSize);
|
|
|
|
|
+ List<FsCourseAnswerLogs> fsCourseAnswerLogs = fsCourseAnswerLogsMapper.selectFsCourseAnswerLogsList(queryLog);
|
|
|
|
|
+
|
|
|
|
|
+ if (fsCourseAnswerLogs == null || fsCourseAnswerLogs.isEmpty()) {
|
|
|
|
|
+ log.info("所有12月之前的数据已迁移完成");
|
|
|
|
|
+ hasMoreData = false;
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ log.info("第{}批查询到{}条数据,开始处理", pageNum, fsCourseAnswerLogs.size());
|
|
|
|
|
+
|
|
|
|
|
+ // 批量处理数据(迁移到目标表并删除源表数据)
|
|
|
|
|
+ int processedCount = processBatchData(fsCourseAnswerLogs);
|
|
|
|
|
+ totalProcessed += processedCount;
|
|
|
|
|
+
|
|
|
|
|
+ log.info("第{}批数据处理完成,成功迁移{}条数据,累计迁移{}条",
|
|
|
|
|
+ pageNum, processedCount, totalProcessed);
|
|
|
|
|
+
|
|
|
|
|
+ pageNum++;
|
|
|
|
|
+
|
|
|
|
|
+ // 清理分页参数,避免影响下次查询
|
|
|
|
|
+ PageHelper.clearPage();
|
|
|
|
|
+
|
|
|
|
|
+ // 小批量提交后稍作休息,避免数据库压力过大
|
|
|
|
|
+ if (hasMoreData) {
|
|
|
|
|
+ Thread.sleep(100);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ log.info("数据迁移任务完成,总计迁移{}条数据", totalProcessed);
|
|
|
|
|
+
|
|
|
|
|
+ } catch (InterruptedException e) {
|
|
|
|
|
+ log.warn("数据迁移任务被中断,已处理{}条数据", totalProcessed);
|
|
|
|
|
+ Thread.currentThread().interrupt();
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("数据迁移过程中发生异常,已处理{}条数据", totalProcessed, e);
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ // 确保清理分页参数
|
|
|
|
|
+ PageHelper.clearPage();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 创建查询条件 - 获取所有12月之前的数据
|
|
|
|
|
+ */
|
|
|
|
|
+ private FsCourseAnswerLogs createQueryCondition() {
|
|
|
|
|
+ FsCourseAnswerLogs queryLog = new FsCourseAnswerLogs();
|
|
|
|
|
+
|
|
|
|
|
+ // 设置开始时间为系统允许的最早时间(可根据实际情况调整)
|
|
|
|
|
+ queryLog.setBeginTime(getEarliestTimeString());
|
|
|
|
|
+
|
|
|
|
|
+ // 设置结束时间为今年12月1日之前(包含历史所有年份的12月之前数据)
|
|
|
|
|
+ queryLog.setEndTime(getDecemberFirstTimeString());
|
|
|
|
|
+
|
|
|
|
|
+ return queryLog;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 批量处理日志数据
|
|
|
|
|
+ * @return 成功处理的数据条数
|
|
|
|
|
+ */
|
|
|
|
|
+ private int processBatchData(List<FsCourseAnswerLogs> logs) {
|
|
|
|
|
+ if (logs == null || logs.isEmpty()) {
|
|
|
|
|
+ return 0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ int successCount = 0;
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ // 1. 批量插入到目标表
|
|
|
|
|
+ log.debug("开始批量插入数据到目标表,数量:{}", logs.size ());
|
|
|
|
|
+ int insertCount = fsCourseAnswerLogsMapper.batchInsert(logs);
|
|
|
|
|
+ log.debug("批量插入完成,插入{}条数据", insertCount);
|
|
|
|
|
+
|
|
|
|
|
+ if (insertCount > 0) {
|
|
|
|
|
+ // 2. 获取成功插入的数据ID
|
|
|
|
|
+ List<Long> idsToDelete = logs.stream()
|
|
|
|
|
+ .limit(insertCount) // 只删除成功插入的数据
|
|
|
|
|
+ .map(FsCourseAnswerLogs::getLogId)
|
|
|
|
|
+ .filter(Objects::nonNull)
|
|
|
|
|
+ .collect(Collectors.toList());
|
|
|
|
|
+
|
|
|
|
|
+ if (!idsToDelete.isEmpty()) {
|
|
|
|
|
+ // 3. 批量删除源表数据
|
|
|
|
|
+ log.debug("开始批量删除源表数据,数量:{}", idsToDelete.size());
|
|
|
|
|
+ int deleteCount = fsCourseAnswerLogsMapper.batchDeleteByIds(idsToDelete);
|
|
|
|
|
+ log.debug("批量删除完成,删除{}条数据", deleteCount);
|
|
|
|
|
+
|
|
|
|
|
+ // 返回成功处理的数量(以插入成功为准)
|
|
|
|
|
+ successCount = insertCount;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("批量处理数据时发生异常", e);
|
|
|
|
|
+ // 这里可以根据需要添加重试逻辑
|
|
|
|
|
+ // retryProcessBatchData(logs);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return successCount;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 检查是否应该停止(凌晨3点之后)
|
|
|
|
|
+ */
|
|
|
|
|
+ private boolean shouldStopAt3AM() {
|
|
|
|
|
+ Calendar calendar = Calendar.getInstance();
|
|
|
|
|
+ int hour = calendar.get(Calendar.HOUR_OF_DAY);
|
|
|
|
|
+ int minute = calendar.get(Calendar.MINUTE);
|
|
|
|
|
+
|
|
|
|
|
+ // 凌晨3点及之后返回true
|
|
|
|
|
+ return hour >= 3;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取最早时间字符串(String格式)
|
|
|
|
|
+ */
|
|
|
|
|
+ private String getEarliestTimeString() {
|
|
|
|
|
+ try {
|
|
|
|
|
+ // 这里可以根据业务设置一个最早的开始时间
|
|
|
|
|
+ // 例如:如果只迁移最近3年的数据
|
|
|
|
|
+ Calendar calendar = Calendar.getInstance();
|
|
|
|
|
+ calendar.add(Calendar.YEAR, -3); // 3年前
|
|
|
|
|
+ calendar.set(Calendar.MONTH, 0);
|
|
|
|
|
+ calendar.set(Calendar.DAY_OF_MONTH, 1);
|
|
|
|
|
+ calendar.set(Calendar.HOUR_OF_DAY, 0);
|
|
|
|
|
+ calendar.set(Calendar.MINUTE, 0);
|
|
|
|
|
+ calendar.set(Calendar.SECOND, 0);
|
|
|
|
|
+ calendar.set(Calendar.MILLISECOND, 0);
|
|
|
|
|
+
|
|
|
|
|
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
|
|
|
|
+ return sdf.format(calendar.getTime());
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("设置最早时间失败,使用默认值null", e);
|
|
|
|
|
+ return null; // 返回null表示不限制最早时间
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取今年12月1日之前的时间字符串
|
|
|
|
|
+ */
|
|
|
|
|
+ private String getDecemberFirstTimeString() {
|
|
|
|
|
+ try {
|
|
|
|
|
+ Calendar calendar = Calendar.getInstance();
|
|
|
|
|
+ int currentYear = calendar.get(Calendar.YEAR);
|
|
|
|
|
+
|
|
|
|
|
+ // 获取当前年份的12月1日
|
|
|
|
|
+ calendar.set(currentYear, Calendar.DECEMBER, 1, 0, 0, 0);
|
|
|
|
|
+ calendar.set(Calendar.MILLISECOND, 0);
|
|
|
|
|
+
|
|
|
|
|
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
|
|
|
|
+ return sdf.format(calendar.getTime());
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("获取12月1日时间失败", e);
|
|
|
|
|
+ // 返回一个很早的时间作为兜底
|
|
|
|
|
+ return "2020-12-01 00:00:00";
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
}
|
|
}
|