diff --git a/backend/services/operator-market-service/src/main/java/com/datamate/operator/application/OperatorService.java b/backend/services/operator-market-service/src/main/java/com/datamate/operator/application/OperatorService.java index f1cb5da..87ed433 100644 --- a/backend/services/operator-market-service/src/main/java/com/datamate/operator/application/OperatorService.java +++ b/backend/services/operator-market-service/src/main/java/com/datamate/operator/application/OperatorService.java @@ -3,6 +3,7 @@ package com.datamate.operator.application; import com.datamate.common.domain.model.ChunkUploadPreRequest; import com.datamate.common.domain.service.FileService; import com.datamate.common.infrastructure.exception.BusinessException; +import com.datamate.common.infrastructure.exception.SystemErrorCode; import com.datamate.operator.domain.contants.OperatorConstant; import com.datamate.operator.infrastructure.converter.OperatorConverter; import com.datamate.operator.domain.model.OperatorView; @@ -21,10 +22,13 @@ import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.MapUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.Resource; +import org.springframework.core.io.UrlResource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.io.File; +import java.net.MalformedURLException; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -165,6 +169,20 @@ public class OperatorService { } } + public Resource downloadExampleOperator(File file) { + try { + Resource resource = new UrlResource(file.toURI()); + if (resource.exists()) { + return resource; + } else { + throw BusinessException.of(SystemErrorCode.RESOURCE_NOT_FOUND); + } + } catch (MalformedURLException ex) { + log.error("File not found: {}", file.getName(), ex); + throw BusinessException.of(SystemErrorCode.RESOURCE_NOT_FOUND); + } + } + private String convertObjectToListString(Object object) { if (object == null) { return null; diff --git a/backend/services/operator-market-service/src/main/java/com/datamate/operator/interfaces/rest/OperatorController.java b/backend/services/operator-market-service/src/main/java/com/datamate/operator/interfaces/rest/OperatorController.java index af02ab2..46a38d1 100644 --- a/backend/services/operator-market-service/src/main/java/com/datamate/operator/interfaces/rest/OperatorController.java +++ b/backend/services/operator-market-service/src/main/java/com/datamate/operator/interfaces/rest/OperatorController.java @@ -1,5 +1,6 @@ package com.datamate.operator.interfaces.rest; +import com.datamate.common.infrastructure.common.IgnoreResponseWrap; import com.datamate.common.interfaces.PagedResponse; import com.datamate.operator.application.OperatorService; import com.datamate.operator.domain.contants.OperatorConstant; @@ -8,9 +9,14 @@ import com.datamate.operator.interfaces.dto.OperatorsListPostRequest; import com.datamate.operator.interfaces.dto.UploadOperatorRequest; import lombok.RequiredArgsConstructor; import org.apache.commons.collections4.CollectionUtils; +import org.springframework.core.io.Resource; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import java.io.File; import java.util.List; @RestController @@ -70,4 +76,23 @@ public class OperatorController { public void operatorDelete(@PathVariable("id") String id) { operatorService.deleteOperator(id); } + + @IgnoreResponseWrap + @GetMapping(value = "/examples/download", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE + ";charset=UTF-8") + public ResponseEntity downloadDatasetFileById() { + try { + File file = new File("/opt/backend/test_operator.tar"); + Resource resource = operatorService.downloadExampleOperator(file); + return ResponseEntity.ok() + .contentType(MediaType.APPLICATION_OCTET_STREAM) + .header(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, HttpHeaders.CONTENT_DISPOSITION) + .header(HttpHeaders.CONTENT_DISPOSITION, + "attachment; filename=\"" + file.getName() + "\"") + .body(resource); + } catch (IllegalArgumentException e) { + return ResponseEntity.status(HttpStatus.NOT_FOUND).build(); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); + } + } } diff --git a/frontend/src/pages/OperatorMarket/Home/OperatorMarket.tsx b/frontend/src/pages/OperatorMarket/Home/OperatorMarket.tsx index 9bc789c..114ce9d 100644 --- a/frontend/src/pages/OperatorMarket/Home/OperatorMarket.tsx +++ b/frontend/src/pages/OperatorMarket/Home/OperatorMarket.tsx @@ -5,6 +5,7 @@ import { EditOutlined, FilterOutlined, PlusOutlined, + DownloadOutlined } from "@ant-design/icons"; import { Boxes } from "lucide-react"; import { SearchControls } from "@/components/SearchControls"; @@ -20,6 +21,7 @@ import { ListView } from "./components/List"; import useFetchData from "@/hooks/useFetchData"; import { deleteOperatorByIdUsingDelete, + downloadExampleOperatorUsingGet, queryCategoryTreeUsingGet, queryOperatorsUsingPost, } from "../operator.api"; @@ -58,6 +60,11 @@ export default function OperatorMarketPage() { navigate(`/data/operator-market/create`); }; + const handleDownload = async () => { + await downloadExampleOperatorUsingGet("test_operator.tar"); + message.success("文件下载成功"); + }; + const handleUpdateOperator = (operator: OperatorI) => { navigate(`/data/operator-market/create/${operator.id}`); }; @@ -119,7 +126,13 @@ export default function OperatorMarketPage() {

算子市场

- + {/**/} +