feat: 脚本批量导出(JSON)和导入接口+清理重复import

This commit is contained in:
mediabot-pt
2026-07-02 12:55:11 +08:00
parent b017f91d9e
commit dc0a9a09d8

View File

@@ -1,9 +1,11 @@
package com.par.api.controller;
import com.par.common.util.HmacUtil;
import com.par.core.dto.AccountDTO;
import com.par.core.dto.ApiResponse;
import com.par.core.dto.SiteConfigDTO;
import com.par.core.entity.Account;
import com.par.core.entity.Psk;
import com.par.core.entity.SiteConfig;
import com.par.core.enums.ConfigStatus;
import com.par.core.enums.TrustLevel;
@@ -11,21 +13,21 @@ import com.par.core.mapper.AccountMapper;
import com.par.core.mapper.ConfigReviewMapper;
import com.par.core.mapper.SiteConfigMapper;
import com.par.core.mapper.SiteMapper;
import com.par.core.entity.Psk;
import com.par.core.service.AccountService;
import com.par.core.service.ConfigService;
import com.par.core.service.PskService;
import com.par.core.storage.CachedStorageService;
import com.par.common.util.HmacUtil;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.*;
/**
* 管理员控制器
@@ -400,6 +402,68 @@ public class AdminController {
return ApiResponse.success();
}
// ==================== 脚本批量导入导出 ====================
/**
* 导出所有站点脚本 JSON:{siteId: scriptContent}
*/
@GetMapping("/scripts/export")
public ResponseEntity<byte[]> exportScripts(HttpServletRequest request) {
checkAdmin(request);
Map<String, String> all = new LinkedHashMap<>();
for (SiteConfig sc : siteConfigMapper.selectList(
new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<SiteConfig>()
.eq(SiteConfig::getDeleted, 0)
.eq(SiteConfig::getIsLatest, true)
.isNotNull(SiteConfig::getScriptStoragePath))) {
try {
String script = storageService.read(sc.getScriptStoragePath());
if (script != null && !script.isBlank()) all.put(sc.getSiteId(), script);
} catch (Exception ignored) {}
}
byte[] json = new com.fasterxml.jackson.databind.ObjectMapper()
.writerWithDefaultPrettyPrinter().writeValueAsBytes(all);
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=scripts-export.json")
.contentType(MediaType.APPLICATION_JSON)
.body(json);
}
/**
* 导入脚本:{siteId: scriptContent},保存到本地存储
*/
@PostMapping("/scripts/import")
public ApiResponse<Map<String, Object>> importScripts(
@RequestBody Map<String, String> payload,
HttpServletRequest request) {
checkAdmin(request);
int ok = 0, fail = 0;
for (Map.Entry<String, String> e : payload.entrySet()) {
String siteId = e.getKey();
String script = e.getValue();
if (script == null || script.isBlank()) continue;
try {
int nextVersion = 1;
List<SiteConfig> approved = siteConfigMapper.selectList(
new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<SiteConfig>()
.eq(SiteConfig::getSiteId, siteId)
.eq(SiteConfig::getDeleted, 0)
.orderByDesc(SiteConfig::getVersion)
.last("limit 1"));
if (!approved.isEmpty()) {
try { nextVersion = Integer.parseInt(approved.get(0).getVersion()) + 1; }
catch (NumberFormatException ex) {}
}
configService.submitConfig(siteId, String.valueOf(nextVersion), script, null);
ok++;
} catch (Exception ex) {
log.error("导入脚本失败 siteId={}: {}", siteId, ex.getMessage());
fail++;
}
}
return ApiResponse.success(Map.of("ok", ok, "fail", fail));
}
private void checkAdmin(HttpServletRequest request) {
TrustLevel level = (TrustLevel) request.getAttribute("trustLevel");
if (level == null || level != TrustLevel.ADMIN) {