From d30614c857263cdc3cb852ccafd4321973cc458a Mon Sep 17 00:00:00 2001 From: dengqichen Date: Sat, 25 Oct 2025 17:04:28 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=94=9F=E6=88=90=E5=90=8E?= =?UTF-8?q?=E7=AB=AF=E6=9C=8D=E5=8A=A1=E4=BB=A3=E7=A0=81=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../impl/RepositoryBranchServiceImpl.java | 132 +++++++++++++++--- .../db/changelog/changes/v1.0.0-schema.sql | 2 +- 2 files changed, 112 insertions(+), 22 deletions(-) diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/RepositoryBranchServiceImpl.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/RepositoryBranchServiceImpl.java index 4fada8dc..b5db4ff9 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/RepositoryBranchServiceImpl.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/RepositoryBranchServiceImpl.java @@ -65,36 +65,126 @@ public class RepositoryBranchServiceImpl extends BaseServiceImpl branches) { - int retryCount = 0; - while (retryCount < MAX_RETRIES) { - try { - for (int i = 0; i < branches.size(); i += BATCH_SIZE) { - int end = Math.min(i + BATCH_SIZE, branches.size()); - List batch = branches.subList(i, end); - repositoryBranchRepository.saveAll(batch); - } - return; - } catch (ObjectOptimisticLockingFailureException e) { - retryCount++; - if (retryCount >= MAX_RETRIES) { - throw e; - } - log.warn("Optimistic locking failure during batch save, retry attempt {}", retryCount); - try { - Thread.sleep(RETRY_DELAY * retryCount); - } catch (InterruptedException ie) { - Thread.currentThread().interrupt(); - throw new RuntimeException("Interrupted during retry delay", ie); + if (branches == null || branches.isEmpty()) { + return; + } + + log.info("开始批量保存 {} 条分支记录", branches.size()); + int successCount = 0; + int failureCount = 0; + List failedBranches = new ArrayList<>(); + + // 分批处理 + for (int i = 0; i < branches.size(); i += BATCH_SIZE) { + int end = Math.min(i + BATCH_SIZE, branches.size()); + List batch = branches.subList(i, end); + + // 批次内逐条保存,避免一条失败导致整批失败 + for (RepositoryBranch branch : batch) { + int retryCount = 0; + boolean saved = false; + + while (retryCount <= MAX_RETRIES && !saved) { + try { + // 数据清洗:截断过长的字段 + sanitizeBranchData(branch); + + // 保存单条记录 + repositoryBranchRepository.save(branch); + successCount++; + saved = true; + + if (retryCount > 0) { + log.info("分支 {} 重试第 {} 次成功", branch.getName(), retryCount); + } + } catch (ObjectOptimisticLockingFailureException e) { + // 乐观锁冲突,重试 + retryCount++; + if (retryCount > MAX_RETRIES) { + failureCount++; + String errorMsg = String.format("%s (乐观锁冲突)", branch.getName()); + failedBranches.add(errorMsg); + log.error("分支 {} 保存失败,乐观锁冲突重试 {} 次后仍失败", branch.getName(), MAX_RETRIES, e); + } else { + log.warn("分支 {} 乐观锁冲突,进行第 {} 次重试", branch.getName(), retryCount); + try { + Thread.sleep(RETRY_DELAY * retryCount); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + log.error("重试延迟被中断", ie); + } + } + } catch (Exception e) { + // 其他异常(如数据过长、约束冲突等),记录日志并跳过 + failureCount++; + String errorMsg = String.format("%s (%s)", branch.getName(), e.getMessage()); + failedBranches.add(errorMsg); + log.error("分支 {} 保存失败: {}, webUrl长度={}", + branch.getName(), + e.getMessage(), + branch.getWebUrl() != null ? branch.getWebUrl().length() : 0, + e); + break; // 非乐观锁异常不重试 + } } } + + log.info("批次 [{}-{}] 保存完成,成功: {}, 失败: {}", + i, end - 1, successCount, failureCount); + } + + // 汇总结果 + log.info("批量保存完成 - 总数: {}, 成功: {}, 失败: {}", + branches.size(), successCount, failureCount); + + if (!failedBranches.isEmpty()) { + log.warn("保存失败的分支列表(前10条): {}", + failedBranches.stream().limit(10).collect(Collectors.toList())); + } + + // 如果全部失败,抛出异常 + if (failureCount == branches.size() && branches.size() > 0) { + throw new BusinessException(ResponseCode.REPOSITORY_BRANCH_SYNC_FAILED, + new Object[]{"所有分支保存失败"}); + } + } + + /** + * 数据清洗:截断过长的字段,避免数据库插入失败 + */ + private void sanitizeBranchData(RepositoryBranch branch) { + // 截断 web_url + if (branch.getWebUrl() != null && branch.getWebUrl().length() > MAX_WEB_URL_LENGTH) { + String original = branch.getWebUrl(); + branch.setWebUrl(original.substring(0, MAX_WEB_URL_LENGTH)); + log.warn("分支 {} 的 web_url 超长({}字符),已截断至 {} 字符", + branch.getName(), original.length(), MAX_WEB_URL_LENGTH); + } + + // 截断 commit_message(TEXT 类型通常足够大,但防御性处理) + if (branch.getCommitMessage() != null && branch.getCommitMessage().length() > 5000) { + branch.setCommitMessage(branch.getCommitMessage().substring(0, 5000) + "..."); + } + + // 截断 commit_author + if (branch.getCommitAuthor() != null && branch.getCommitAuthor().length() > 100) { + branch.setCommitAuthor(branch.getCommitAuthor().substring(0, 100)); } } diff --git a/backend/src/main/resources/db/changelog/changes/v1.0.0-schema.sql b/backend/src/main/resources/db/changelog/changes/v1.0.0-schema.sql index c79b7ec9..7312d2cb 100644 --- a/backend/src/main/resources/db/changelog/changes/v1.0.0-schema.sql +++ b/backend/src/main/resources/db/changelog/changes/v1.0.0-schema.sql @@ -353,7 +353,7 @@ CREATE TABLE deploy_repo_branch commit_date DATETIME(6) NULL COMMENT '最新提交时间', last_update_time DATETIME(6) NULL COMMENT '分支最后更新时间', last_commit_time DATETIME(6) NULL COMMENT '分支最后提交时间', - web_url VARCHAR(255) NULL COMMENT '网页URL', + web_url VARCHAR(1000) NULL COMMENT '网页URL', project_id BIGINT NOT NULL COMMENT '所属项目ID', external_system_id BIGINT NOT NULL COMMENT '外部系统ID', repo_project_id BIGINT NOT NULL COMMENT 'GitLab的真实project_id',