feat(sourcemap): 上传后自动裁剪,每版本保留最新 3 条,最多保留 5 个版本

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
这个提交包含在:
XuqmGroup 2026-06-23 11:38:21 +08:00
父节点 523f9d7894
当前提交 d02cd10714
共有 2 个文件被更改,包括 59 次插入0 次删除

查看文件

@ -20,6 +20,14 @@ public interface LogSourcemapRepository extends JpaRepository<LogSourcemapEntity
Optional<LogSourcemapEntity> findFirstByAppKeyAndPlatformAndAppVersionAndBundleNameOrderByUploadedAtDesc(
String appKey, String platform, String appVersion, String bundleName);
/** All records for one version, newest first — used for per-version retention pruning. */
List<LogSourcemapEntity> findByAppKeyAndPlatformAndAppVersionAndBundleNameOrderByUploadedAtDesc(
String appKey, String platform, String appVersion, String bundleName);
/** All records for one (appKey, platform, bundleName), newest first — used for version-count pruning. */
List<LogSourcemapEntity> findByAppKeyAndPlatformAndBundleNameOrderByUploadedAtDesc(
String appKey, String platform, String bundleName);
List<LogSourcemapEntity> findByAppKey(String appKey);
void deleteByAppKey(String appKey);

查看文件

@ -16,6 +16,8 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
@ -103,6 +105,8 @@ public class SourcemapService {
log.info("Sourcemap uploaded: appKey={} platform={} version={} buildId={} bundle={}",
appKey, platform, versionKey, buildId, bundleName);
pruneSourcemaps(appKey, platform, appVersion, bundleName);
return new SourcemapUploadResponse(
entity.getId(), entity.getAppKey(), entity.getPlatform(),
entity.getAppVersion(), entity.getBundleName(), entity.getBuildId(), entity.getStorageKey()
@ -157,6 +161,53 @@ public class SourcemapService {
return findSourceMap(appKey, platform, appVersion, null, bundleName);
}
/**
* Enforce retention policy after every upload:
* - per version (appVersion): keep at most 3 records (newest by uploadedAt)
* - per (appKey, platform, bundleName): keep at most 5 distinct appVersions (newest by latest upload)
*/
private void pruneSourcemaps(String appKey, String platform, String appVersion, String bundleName) {
// 1. Per-version limit: keep newest 3 buildIds
List<LogSourcemapEntity> versionRecords = sourcemapRepository
.findByAppKeyAndPlatformAndAppVersionAndBundleNameOrderByUploadedAtDesc(
appKey, platform, appVersion, bundleName);
if (versionRecords.size() > 3) {
deleteEntities(versionRecords.subList(3, versionRecords.size()));
}
// 2. Version-count limit: keep at most 5 distinct appVersions
List<LogSourcemapEntity> allRecords = sourcemapRepository
.findByAppKeyAndPlatformAndBundleNameOrderByUploadedAtDesc(appKey, platform, bundleName);
// Preserve insertion order = newest-first version order
LinkedHashSet<String> orderedVersions = new LinkedHashSet<>();
for (LogSourcemapEntity r : allRecords) {
orderedVersions.add(r.getAppVersion());
}
if (orderedVersions.size() > 5) {
List<String> versionList = new ArrayList<>(orderedVersions);
Set<String> versionsToDelete = new LinkedHashSet<>(versionList.subList(5, versionList.size()));
List<LogSourcemapEntity> excess = allRecords.stream()
.filter(r -> versionsToDelete.contains(r.getAppVersion()))
.toList();
deleteEntities(excess);
log.info("Pruned {} old-version sourcemap(s) for appKey={} platform={} bundle={}",
excess.size(), appKey, platform, bundleName);
}
}
private void deleteEntities(List<LogSourcemapEntity> entities) {
for (LogSourcemapEntity entity : entities) {
try {
Files.deleteIfExists(Paths.get(entity.getStorageKey()));
} catch (IOException e) {
log.warn("Could not delete sourcemap file {}: {}", entity.getStorageKey(), e.getMessage());
}
sourcemapRepository.delete(entity);
}
}
public String readSourceMapContent(String storageKey) throws IOException {
return Files.readString(Paths.get(storageKey));
}