|
|
@@ -0,0 +1,216 @@
|
|
|
+package com.fs.delete.service;
|
|
|
+
|
|
|
+import com.fs.delete.mapper.DataCleanMapper;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.transaction.TransactionDefinition;
|
|
|
+import org.springframework.transaction.TransactionStatus;
|
|
|
+import org.springframework.transaction.support.DefaultTransactionDefinition;
|
|
|
+import org.springframework.jdbc.datasource.DataSourceTransactionManager;
|
|
|
+
|
|
|
+import java.util.Arrays;
|
|
|
+import java.util.List;
|
|
|
+
|
|
|
+@Slf4j
|
|
|
+@Service
|
|
|
+public class DataCleanService {
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private DataCleanMapper dataCleanMapper;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private DataSourceTransactionManager transactionManager;
|
|
|
+
|
|
|
+ // 每批处理用户数量
|
|
|
+// private static final int BATCH_SIZE = 1000;
|
|
|
+ private static final int BATCH_SIZE = 200;
|
|
|
+ // 项目ID
|
|
|
+ private static final Long PROJECT_ID = 60L;
|
|
|
+ // 日期限制
|
|
|
+ private static final String DATE_LIMIT = "2026-03-15";
|
|
|
+ // 备份表后缀
|
|
|
+ private static final String BACKUP_SUFFIX = "_20260324_bak";
|
|
|
+ // 拉黑状态列表
|
|
|
+ private static final List<Integer> BLACK_STATUS_LIST = Arrays.asList(0, 2);
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 执行数据清理
|
|
|
+ */
|
|
|
+ public void cleanData() {
|
|
|
+ log.info("========== 开始数据清理任务 ==========");
|
|
|
+ long startTime = System.currentTimeMillis();
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 1. 处理拉黑用户
|
|
|
+ processBlackUsers();
|
|
|
+
|
|
|
+ // 2. 处理0观看用户
|
|
|
+ processZeroWatchUsers();
|
|
|
+
|
|
|
+ long endTime = System.currentTimeMillis();
|
|
|
+ log.info("========== 数据清理任务完成,总耗时: {} 秒 ==========", (endTime - startTime) / 1000);
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("数据清理任务失败", e);
|
|
|
+ throw new RuntimeException("数据清理任务失败", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理拉黑用户
|
|
|
+ */
|
|
|
+ private void processBlackUsers() {
|
|
|
+ log.info("开始处理拉黑用户");
|
|
|
+
|
|
|
+ int batchNo = 0;
|
|
|
+ int totalDeleted = 0;
|
|
|
+ int totalCount = dataCleanMapper.countBlackUsers(PROJECT_ID, BLACK_STATUS_LIST);
|
|
|
+ log.info("拉黑用户总数: {}", totalCount);
|
|
|
+
|
|
|
+ while (true) {
|
|
|
+ // 1. 查询一批需要删除的用户ID(1000个)
|
|
|
+ List<Long> userIds = dataCleanMapper.selectBlackUserIds(PROJECT_ID, BLACK_STATUS_LIST, BATCH_SIZE);
|
|
|
+
|
|
|
+ if (userIds.isEmpty()) {
|
|
|
+ log.info("拉黑用户处理完成,共处理 {} 批", batchNo);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ batchNo++;
|
|
|
+ log.info("========== 处理第 {} 批拉黑用户,数量: {} ==========", batchNo, userIds.size());
|
|
|
+
|
|
|
+ // 2. 处理这一批用户(备份+删除)
|
|
|
+ processUserBatch(userIds);
|
|
|
+
|
|
|
+ totalDeleted += userIds.size();
|
|
|
+ log.info("第 {} 批处理完成,累计删除: {}/{}", batchNo, totalDeleted, totalCount);
|
|
|
+ log.info("========================================\n");
|
|
|
+
|
|
|
+// break;
|
|
|
+ }
|
|
|
+
|
|
|
+ log.info("拉黑用户处理完成,共删除: {} 条", totalDeleted);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理0观看用户
|
|
|
+ */
|
|
|
+ private void processZeroWatchUsers() {
|
|
|
+ log.info("开始处理0观看用户");
|
|
|
+
|
|
|
+ int batchNo = 0;
|
|
|
+ int totalDeleted = 0;
|
|
|
+// int totalCount = dataCleanMapper.countZeroWatchUsers(PROJECT_ID, DATE_LIMIT);
|
|
|
+// log.info("0观看用户总数: {}", totalCount);
|
|
|
+
|
|
|
+ while (true) {
|
|
|
+ // 1. 查询一批需要删除的用户ID(1000个)
|
|
|
+ List<Long> userIds = dataCleanMapper.selectZeroWatchUserIds(PROJECT_ID, DATE_LIMIT, BATCH_SIZE);
|
|
|
+
|
|
|
+ if (userIds.isEmpty()) {
|
|
|
+ log.info("0观看用户处理完成,共处理 {} 批", batchNo);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ batchNo++;
|
|
|
+ log.info("========== 处理第 {} 批0观看用户,数量: {} ==========", batchNo, userIds.size());
|
|
|
+
|
|
|
+ // 2. 处理这一批用户(备份+删除)
|
|
|
+ processUserBatch(userIds);
|
|
|
+
|
|
|
+ totalDeleted += userIds.size();
|
|
|
+ log.info("第 {} 批处理完成,累计删除: {}/{}", batchNo, totalDeleted);
|
|
|
+ log.info("==========================================\n");
|
|
|
+ }
|
|
|
+
|
|
|
+ log.info("0观看用户处理完成,共删除: {} 条", totalDeleted);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理单批用户(备份+删除)
|
|
|
+ * 删除顺序:先删除答题记录 -> 再删除红包记录 -> 再删除看课记录 -> 最后删除用户关系
|
|
|
+ */
|
|
|
+ private void processUserBatch(List<Long> userIds) {
|
|
|
+ if (userIds == null || userIds.isEmpty()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 开启新事务,确保这一批用户的数据要么全部成功,要么全部回滚
|
|
|
+ DefaultTransactionDefinition def = new DefaultTransactionDefinition();
|
|
|
+ def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
|
|
|
+ TransactionStatus transactionStatus = transactionManager.getTransaction(def);
|
|
|
+
|
|
|
+ try {
|
|
|
+ log.info("开始处理 {} 个用户的数据备份和删除", userIds.size());
|
|
|
+
|
|
|
+ // 1. 备份用户相关数据
|
|
|
+ backupUserData(userIds);
|
|
|
+
|
|
|
+ // 2. 删除答题记录(通过关联看课记录表)
|
|
|
+ int answerDeleted = dataCleanMapper.deleteAnswerLogs(PROJECT_ID, userIds);
|
|
|
+ log.info("删除答题记录: {} 条", answerDeleted);
|
|
|
+
|
|
|
+ // 3. 删除红包记录(通过关联看课记录表)
|
|
|
+ int redPacketDeleted = dataCleanMapper.deleteRedPacketLogs(PROJECT_ID, userIds);
|
|
|
+ log.info("删除红包记录: {} 条", redPacketDeleted);
|
|
|
+
|
|
|
+ // 4. 删除看课记录
|
|
|
+ int watchDeleted = dataCleanMapper.deleteWatchLogs(PROJECT_ID, userIds);
|
|
|
+ log.info("删除看课记录: {} 条", watchDeleted);
|
|
|
+
|
|
|
+ // 5. 删除用户关系
|
|
|
+ int userDeleted = dataCleanMapper.deleteUserCompanyUser(PROJECT_ID, userIds);
|
|
|
+ log.info("删除用户关系: {} 条", userDeleted);
|
|
|
+
|
|
|
+ // 提交事务,这一批用户的所有操作完成
|
|
|
+ transactionManager.commit(transactionStatus);
|
|
|
+ log.info("{} 个用户处理完成", userIds.size());
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 如果这一批有任何失败,全部回滚
|
|
|
+ transactionManager.rollback(transactionStatus);
|
|
|
+ log.error("处理用户批次失败,将回滚所有操作,用户数量: {}", userIds.size(), e);
|
|
|
+ throw new RuntimeException("处理用户批次失败", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 备份用户相关数据
|
|
|
+ */
|
|
|
+ private void backupUserData(List<Long> userIds) {
|
|
|
+ // 1. 备份用户关系表
|
|
|
+ int userCount = dataCleanMapper.backupUserCompanyUser(BACKUP_SUFFIX, PROJECT_ID, userIds);
|
|
|
+ log.debug("备份用户关系: {} 条", userCount);
|
|
|
+
|
|
|
+ // 2. 备份看课记录表
|
|
|
+ int watchCount = dataCleanMapper.backupCourseWatchLog(BACKUP_SUFFIX, PROJECT_ID, userIds);
|
|
|
+ log.debug("备份看课记录: {} 条", watchCount);
|
|
|
+
|
|
|
+ // 3. 备份答题记录表(关联看课记录)
|
|
|
+ int answerCount = dataCleanMapper.backupCourseAnswerLogs(BACKUP_SUFFIX, PROJECT_ID, userIds);
|
|
|
+ log.debug("备份答题记录: {} 条", answerCount);
|
|
|
+
|
|
|
+ // 4. 备份红包记录表(关联看课记录)
|
|
|
+ int redPacketCount = dataCleanMapper.backupCourseRedPacketLog(BACKUP_SUFFIX, PROJECT_ID, userIds);
|
|
|
+ log.debug("备份红包记录: {} 条", redPacketCount);
|
|
|
+
|
|
|
+ log.info("备份完成 - 用户关系: {}, 看课: {}, 答题: {}, 红包: {}",
|
|
|
+ userCount, watchCount, answerCount, redPacketCount);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 打印统计数据
|
|
|
+ */
|
|
|
+ public void printStatistics() {
|
|
|
+ log.info("========== 统计数据 ==========");
|
|
|
+
|
|
|
+ int blackCount = dataCleanMapper.countBlackUsers(PROJECT_ID, BLACK_STATUS_LIST);
|
|
|
+ int zeroCount = dataCleanMapper.countZeroWatchUsers(PROJECT_ID, DATE_LIMIT);
|
|
|
+
|
|
|
+ log.info("拉黑用户数量: {}", blackCount);
|
|
|
+ log.info("0观看用户数量: {}", zeroCount);
|
|
|
+ log.info("总计需要删除: {}", (blackCount + zeroCount));
|
|
|
+ log.info("==============================");
|
|
|
+ }
|
|
|
+}
|