Develop op (#35)

* refactor: enhance CleaningTaskService and related components with validation and repository updates
* feature: 支持算子上传创建
This commit is contained in:
hhhhsc701
2025-10-30 17:17:00 +08:00
committed by GitHub
parent 8d2b41ed94
commit b9b97c1ac2
63 changed files with 1190 additions and 1177 deletions

View File

@@ -82,8 +82,8 @@ deer-flow-docker-build:
git clone git@github.com:bytedance/deer-flow.git ../deer-flow; \
fi
sed -i "s/dark/light/g" "../deer-flow/web/src/components/deer-flow/theme-provider-wrapper.tsx"
cp deployment/docker/deer-flow/.env.example ../deer-flow/.env
cp deployment/docker/deer-flow/conf.yaml.example ../deer-flow/conf.yaml
cp -n deployment/docker/deer-flow/.env.example ../deer-flow/.env
cp -n deployment/docker/deer-flow/conf.yaml.example ../deer-flow/conf.yaml
cd ../deer-flow && docker compose build
.PHONY: mineru-docker-build
@@ -131,16 +131,16 @@ mineru-k8s-uninstall:
.PHONY: datamate-docker-install
datamate-docker-install:
cd deployment/docker/datamate && cp .env.example .env && docker compose -f docker-compose.yml up -d
cd deployment/docker/datamate && cp -n .env.example .env && docker compose -f docker-compose.yml up -d
.PHONY: datamate-docker-uninstall
datamate-docker-uninstall:
cd deployment/docker/datamate && docker compose -f docker-compose.yml down
cd deployment/docker/datamate && docker compose -f docker-compose.yml down -v
.PHONY: deer-flow-docker-install
deer-flow-docker-install:
cd deployment/docker/datamate && cp .env.deer-flow.example .env && docker compose -f docker-compose.yml up -d
cd deployment/docker/deer-flow && cp .env.example .env && cp conf.yaml.example conf.yaml && docker compose -f docker-compose.yml up -d
cd deployment/docker/datamate && cp -n .env.deer-flow.example .env && docker compose -f docker-compose.yml up -d
cd deployment/docker/deer-flow && cp -n .env.example .env && cp -n conf.yaml.example conf.yaml && docker compose -f docker-compose.yml up -d
.PHONY: deer-flow-docker-uninstall
deer-flow-docker-uninstall:

File diff suppressed because it is too large Load Diff

View File

@@ -27,6 +27,11 @@
<artifactId>data-management-service</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.datamate</groupId>
<artifactId>operator-market-service</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>

View File

@@ -1,6 +1,6 @@
package com.datamate.cleaning;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
@@ -8,9 +8,11 @@ import org.springframework.scheduling.annotation.EnableScheduling;
* 数据归集服务配置类
* 基于DataX的数据归集和同步服务,支持多种数据源的数据采集和归集
*/
@SpringBootApplication
@EnableAsync
@EnableScheduling
@ComponentScan(basePackages = {
"com.datamate.cleaning"
})
public class DataCleaningServiceConfiguration {
// Configuration class for JAR packaging - no main method needed
}

View File

@@ -10,6 +10,7 @@ import com.datamate.cleaning.domain.repository.CleaningResultRepository;
import com.datamate.cleaning.domain.repository.CleaningTaskRepository;
import com.datamate.cleaning.domain.repository.OperatorInstanceRepository;
import com.datamate.cleaning.infrastructure.validator.CleanTaskValidator;
import com.datamate.cleaning.interfaces.dto.CleaningProcess;
import com.datamate.cleaning.interfaces.dto.CleaningTaskDto;
import com.datamate.cleaning.interfaces.dto.CreateCleaningTaskRequest;
@@ -59,6 +60,8 @@ public class CleaningTaskService {
private final DatasetFileApplicationService datasetFileService;
private final CleanTaskValidator cleanTaskValidator;
private final String DATASET_PATH = "/dataset";
private final String FLOW_PATH = "/flow";
@@ -80,6 +83,9 @@ public class CleaningTaskService {
@Transactional
public CleaningTaskDto createTask(CreateCleaningTaskRequest request) {
cleanTaskValidator.checkNameDuplication(request.getName());
cleanTaskValidator.checkInputAndOutput(request.getInstance());
CreateDatasetRequest createDatasetRequest = new CreateDatasetRequest();
createDatasetRequest.setName(request.getDestDatasetName());
createDatasetRequest.setDatasetType(DatasetType.valueOf(request.getDestDatasetType()));

View File

@@ -5,6 +5,8 @@ import com.datamate.cleaning.domain.repository.CleaningTemplateRepository;
import com.datamate.cleaning.domain.repository.OperatorInstanceRepository;
import com.datamate.cleaning.interfaces.dto.*;
import com.datamate.cleaning.domain.model.entity.TemplateWithInstance;
import com.datamate.operator.domain.repository.OperatorRepository;
import com.datamate.operator.interfaces.dto.OperatorDto;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
@@ -24,8 +26,10 @@ public class CleaningTemplateService {
private final OperatorInstanceRepository operatorInstanceRepo;
private final OperatorRepository operatorRepo;
public List<CleaningTemplateDto> getTemplates(String keywords) {
List<OperatorDto> allOperators = operatorInstanceRepo.findAllOperators();
List<OperatorDto> allOperators = operatorRepo.findAllOperators();
Map<String, OperatorDto> operatorsMap = allOperators.stream()
.collect(Collectors.toMap(OperatorDto::getId, Function.identity()));
List<TemplateWithInstance> allTemplates = cleaningTemplateRepo.findAllTemplates(keywords);

View File

@@ -12,7 +12,7 @@ public enum CleanErrorCode implements ErrorCode {
*/
DUPLICATE_TASK_NAME("clean.0001", "清洗任务名称重复"),
CREATE_DATASET_FAILED("clean.0002", "创建数据集失败");
IN_AND_OUT_NOT_MATCH("clean.0002", "算子输入输出不匹配");
private final String code;
private final String message;

View File

@@ -1,26 +0,0 @@
package com.datamate.cleaning.domain.model;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.util.List;
@Getter
@Setter
@NoArgsConstructor
public class CreateDatasetRequest {
/** 数据集名称 */
private String name;
/** 数据集描述 */
private String description;
/** 数据集类型 */
private String datasetType;
/** 标签列表 */
private List<String> tags;
/** 数据源 */
private String dataSource;
/** 目标位置 */
private String targetLocation;
}

View File

@@ -1,36 +0,0 @@
package com.datamate.cleaning.domain.model;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.time.LocalDateTime;
@Getter
@Setter
@NoArgsConstructor
public class DatasetFileResponse {
/** 文件ID */
private String id;
/** 文件名 */
private String fileName;
/** 原始文件名 */
private String originalName;
/** 文件类型 */
private String fileType;
/** 文件大小(字节) */
private Long fileSize;
/** 文件状态 */
private String status;
/** 文件描述 */
private String description;
/** 文件路径 */
private String filePath;
/** 上传时间 */
private LocalDateTime uploadTime;
/** 最后更新时间 */
private LocalDateTime lastAccessTime;
/** 上传者 */
private String uploadedBy;
}

View File

@@ -1,44 +0,0 @@
package com.datamate.cleaning.domain.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.time.LocalDateTime;
/**
* 数据集实体(与数据库表 t_dm_datasets 对齐)
*/
@Getter
@Setter
@NoArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class DatasetResponse {
/** 数据集ID */
private String id;
/** 数据集名称 */
private String name;
/** 数据集描述 */
private String description;
/** 数据集类型 */
private String datasetType;
/** 数据集状态 */
private String status;
/** 数据源 */
private String dataSource;
/** 目标位置 */
private String targetLocation;
/** 文件数量 */
private Integer fileCount;
/** 总大小(字节) */
private Long totalSize;
/** 完成率(0-100) */
private Float completionRate;
/** 创建时间 */
private LocalDateTime createdAt;
/** 更新时间 */
private LocalDateTime updatedAt;
/** 创建者 */
private String createdBy;
}

View File

@@ -1,23 +0,0 @@
package com.datamate.cleaning.domain.model;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
/**
* 数据集类型响应DTO
*/
@Getter
@Setter
public class DatasetTypeResponse {
/** 类型编码 */
private String code;
/** 类型名称 */
private String name;
/** 类型描述 */
private String description;
/** 支持的文件格式 */
private List<String> supportedFormats;
/** 图标 */
private String icon;
}

View File

@@ -1,28 +0,0 @@
package com.datamate.cleaning.domain.model;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.util.List;
@Getter
@Setter
@NoArgsConstructor
public class PagedDatasetFileResponse {
/** 文件内容列表 */
private List<DatasetFileResponse> content;
/** 当前页码 */
private Integer page;
/** 每页大小 */
private Integer size;
/** 总元素数 */
private Integer totalElements;
/** 总页数 */
private Integer totalPages;
/** 是否为第一页 */
private Boolean first;
/** 是否为最后一页 */
private Boolean last;
}

View File

@@ -16,4 +16,6 @@ public interface CleaningTaskRepository extends IRepository<CleaningTask> {
void updateTask(CleaningTaskDto task);
void deleteTaskById(String taskId);
boolean isNameExist(String name);
}

View File

@@ -1,15 +1,12 @@
package com.datamate.cleaning.domain.repository;
import com.baomidou.mybatisplus.extension.repository.IRepository;
import com.datamate.cleaning.interfaces.dto.OperatorDto;
import com.datamate.cleaning.interfaces.dto.OperatorInstanceDto;
import com.datamate.cleaning.domain.model.entity.OperatorInstance;
import java.util.List;
public interface OperatorInstanceRepository extends IRepository<OperatorInstance> {
List<OperatorDto> findAllOperators();
void insertInstance(String instanceId, List<OperatorInstanceDto> instances);
void deleteByInstanceId(String instanceId);

View File

@@ -3,10 +3,10 @@ package com.datamate.cleaning.infrastructure.converter;
import com.datamate.cleaning.domain.model.entity.OperatorInstance;
import com.datamate.cleaning.domain.model.entity.Operator;
import com.datamate.cleaning.interfaces.dto.OperatorDto;
import com.datamate.cleaning.interfaces.dto.OperatorInstanceDto;
import com.datamate.common.infrastructure.exception.BusinessException;
import com.datamate.common.infrastructure.exception.SystemErrorCode;
import com.datamate.operator.interfaces.dto.OperatorDto;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.mapstruct.Mapper;

View File

@@ -51,4 +51,10 @@ public class CleaningTaskRepositoryImpl extends CrudRepository<CleaningTaskMappe
public void deleteTaskById(String taskId) {
mapper.deleteById(taskId);
}
public boolean isNameExist(String name) {
LambdaQueryWrapper<CleaningTask> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(CleaningTask::getName, name);
return mapper.exists(queryWrapper);
}
}

View File

@@ -3,7 +3,6 @@ package com.datamate.cleaning.infrastructure.persistence.Impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.repository.CrudRepository;
import com.datamate.cleaning.infrastructure.converter.OperatorInstanceConverter;
import com.datamate.cleaning.interfaces.dto.OperatorDto;
import com.datamate.cleaning.interfaces.dto.OperatorInstanceDto;
import com.datamate.cleaning.domain.model.entity.OperatorInstance;
import com.datamate.cleaning.domain.repository.OperatorInstanceRepository;
@@ -20,11 +19,6 @@ public class OperatorInstanceRepositoryImpl extends CrudRepository<OperatorInsta
implements OperatorInstanceRepository {
private final OperatorInstanceMapper mapper;
@Override
public List<OperatorDto> findAllOperators() {
return OperatorInstanceConverter.INSTANCE.fromEntityToDto(mapper.findAllOperators());
}
@Override
public void insertInstance(String instanceId, List<OperatorInstanceDto> instances) {
List<OperatorInstance> operatorInstances = new ArrayList<>();

View File

@@ -1,17 +1,10 @@
package com.datamate.cleaning.infrastructure.persistence.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.datamate.cleaning.domain.model.entity.Operator;
import com.datamate.cleaning.domain.model.entity.OperatorInstance;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface OperatorInstanceMapper extends BaseMapper<OperatorInstance> {
@Select("SELECT id, name, description, version, inputs, outputs, runtime, settings, is_star, created_at, " +
"updated_at FROM t_operator")
List<Operator> findAllOperators();
}

View File

@@ -0,0 +1,40 @@
package com.datamate.cleaning.infrastructure.validator;
import com.datamate.cleaning.common.exception.CleanErrorCode;
import com.datamate.cleaning.domain.repository.CleaningTaskRepository;
import com.datamate.cleaning.interfaces.dto.OperatorInstanceDto;
import com.datamate.common.infrastructure.exception.BusinessException;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Locale;
@Component
@RequiredArgsConstructor
public class CleanTaskValidator {
private final CleaningTaskRepository cleaningTaskRepo;
public void checkNameDuplication (String name) {
if (cleaningTaskRepo.isNameExist(name)) {
throw BusinessException.of(CleanErrorCode.DUPLICATE_TASK_NAME);
}
}
public void checkInputAndOutput (List<OperatorInstanceDto> operators) {
if (operators == null || operators.size() <= 1) {
return;
}
for (int i = 1; i < operators.size(); i++) {
OperatorInstanceDto front = operators.get(i - 1);
OperatorInstanceDto back = operators.get(i);
if (!StringUtils.equals(front.getOutputs(), back.getInputs())) {
throw BusinessException.of(CleanErrorCode.IN_AND_OUT_NOT_MATCH,
String.format(Locale.ROOT, "ops(name: [%s, %s]) inputs and outputs does not match",
front.getName(), back.getName()));
}
}
}
}

View File

@@ -5,6 +5,7 @@ import com.datamate.cleaning.common.enums.CleaningTaskStatusEnum;
import java.time.LocalDateTime;
import java.util.List;
import com.datamate.operator.interfaces.dto.OperatorDto;
import lombok.Getter;
import lombok.Setter;
import org.springframework.format.annotation.DateTimeFormat;

View File

@@ -4,6 +4,7 @@ import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import com.datamate.operator.interfaces.dto.OperatorDto;
import lombok.Getter;
import lombok.Setter;
import org.springframework.format.annotation.DateTimeFormat;

View File

@@ -1,41 +0,0 @@
package com.datamate.cleaning.interfaces.dto;
import java.time.LocalDateTime;
import lombok.Getter;
import lombok.Setter;
import org.springframework.format.annotation.DateTimeFormat;
/**
* OperatorDto
*/
@Getter
@Setter
public class OperatorDto {
private String id;
private String name;
private String description;
private String version;
private String inputs;
private String outputs;
private String runtime;
private String settings;
private Boolean isStar;
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
private LocalDateTime createdAt;
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
private LocalDateTime updatedAt;
}

View File

@@ -1,6 +1,7 @@
package com.datamate.cleaning.interfaces.dto;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -17,6 +18,14 @@ public class OperatorInstanceDto {
private String id;
private String name;
private String inputs;
private String outputs;
private List<Integer> categories;
private Map<String, Object> overrides = new HashMap<>();
}

View File

@@ -3,10 +3,8 @@ package com.datamate.cleaning.interfaces.rest;
import com.datamate.cleaning.application.CleaningTaskService;
import com.datamate.cleaning.interfaces.dto.CleaningTaskDto;
import com.datamate.cleaning.interfaces.dto.CreateCleaningTaskRequest;
import com.datamate.common.infrastructure.common.Response;
import com.datamate.common.interfaces.PagedResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@@ -19,41 +17,41 @@ public class CleaningTaskController {
private final CleaningTaskService cleaningTaskService;
@GetMapping
public ResponseEntity<Response<PagedResponse<CleaningTaskDto>>> cleaningTasksGet(
public PagedResponse<CleaningTaskDto> cleaningTasksGet(
@RequestParam("page") Integer page,
@RequestParam("size") Integer size, @RequestParam(value = "status", required = false) String status,
@RequestParam(value = "keywords", required = false) String keywords) {
List<CleaningTaskDto> tasks = cleaningTaskService.getTasks(status, keywords, page, size);
int count = cleaningTaskService.countTasks(status, keywords);
int totalPages = (count + size + 1) / size;
return ResponseEntity.ok(Response.ok(PagedResponse.of(tasks, page, count, totalPages)));
return PagedResponse.of(tasks, page, count, totalPages);
}
@PostMapping
public ResponseEntity<Response<CleaningTaskDto>> cleaningTasksPost(@RequestBody CreateCleaningTaskRequest request) {
return ResponseEntity.ok(Response.ok(cleaningTaskService.createTask(request)));
public CleaningTaskDto cleaningTasksPost(@RequestBody CreateCleaningTaskRequest request) {
return cleaningTaskService.createTask(request);
}
@PostMapping("/{taskId}/stop")
public ResponseEntity<Response<Object>> cleaningTasksStop(@PathVariable("taskId") String taskId) {
public String cleaningTasksStop(@PathVariable("taskId") String taskId) {
cleaningTaskService.stopTask(taskId);
return ResponseEntity.ok(Response.ok(null));
return taskId;
}
@PostMapping("/{taskId}/execute")
public ResponseEntity<Response<Object>> cleaningTasksStart(@PathVariable("taskId") String taskId) {
public String cleaningTasksStart(@PathVariable("taskId") String taskId) {
cleaningTaskService.executeTask(taskId);
return ResponseEntity.ok(Response.ok(null));
return taskId;
}
@GetMapping("/{taskId}")
public ResponseEntity<Response<CleaningTaskDto>> cleaningTasksTaskIdGet(@PathVariable("taskId") String taskId) {
return ResponseEntity.ok(Response.ok(cleaningTaskService.getTask(taskId)));
public CleaningTaskDto cleaningTasksTaskIdGet(@PathVariable("taskId") String taskId) {
return cleaningTaskService.getTask(taskId);
}
@DeleteMapping("/{taskId}")
public ResponseEntity<Response<Object>> cleaningTasksTaskIdDelete(@PathVariable("taskId") String taskId) {
public String cleaningTasksTaskIdDelete(@PathVariable("taskId") String taskId) {
cleaningTaskService.deleteTask(taskId);
return ResponseEntity.ok(Response.ok(null));
return taskId;
}
}

View File

@@ -4,10 +4,8 @@ import com.datamate.cleaning.application.CleaningTemplateService;
import com.datamate.cleaning.interfaces.dto.CleaningTemplateDto;
import com.datamate.cleaning.interfaces.dto.CreateCleaningTemplateRequest;
import com.datamate.cleaning.interfaces.dto.UpdateCleaningTemplateRequest;
import com.datamate.common.infrastructure.common.Response;
import com.datamate.common.interfaces.PagedResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@@ -29,14 +27,14 @@ public class CleaningTemplateController {
private final CleaningTemplateService cleaningTemplateService;
@GetMapping
public ResponseEntity<Response<PagedResponse<CleaningTemplateDto>>> cleaningTemplatesGet(
public PagedResponse<CleaningTemplateDto> cleaningTemplatesGet(
@RequestParam(value = "page", required = false) Integer page,
@RequestParam(value = "size", required = false) Integer size,
@RequestParam(value = "keywords", required = false) String keyword) {
List<CleaningTemplateDto> templates = cleaningTemplateService.getTemplates(keyword);
if (page == null || size == null) {
return ResponseEntity.ok(Response.ok(PagedResponse.of(templates.stream()
.sorted(Comparator.comparing(CleaningTemplateDto::getCreatedAt).reversed()).toList())));
return PagedResponse.of(templates.stream()
.sorted(Comparator.comparing(CleaningTemplateDto::getCreatedAt).reversed()).toList());
}
int count = templates.size();
int totalPages = (count + size + 1) / size;
@@ -44,31 +42,31 @@ public class CleaningTemplateController {
.sorted(Comparator.comparing(CleaningTemplateDto::getCreatedAt).reversed())
.skip((long) page * size)
.limit(size).toList();
return ResponseEntity.ok(Response.ok(PagedResponse.of(limitTemplates, page, count, totalPages)));
return PagedResponse.of(limitTemplates, page, count, totalPages);
}
@PostMapping
public ResponseEntity<Response<CleaningTemplateDto>> cleaningTemplatesPost(
public CleaningTemplateDto cleaningTemplatesPost(
@RequestBody CreateCleaningTemplateRequest request) {
return ResponseEntity.ok(Response.ok(cleaningTemplateService.createTemplate(request)));
return cleaningTemplateService.createTemplate(request);
}
@GetMapping("/{templateId}")
public ResponseEntity<Response<CleaningTemplateDto>> cleaningTemplatesTemplateIdGet(
public CleaningTemplateDto cleaningTemplatesTemplateIdGet(
@PathVariable("templateId") String templateId) {
return ResponseEntity.ok(Response.ok(cleaningTemplateService.getTemplate(templateId)));
return cleaningTemplateService.getTemplate(templateId);
}
@PutMapping("/{templateId}")
public ResponseEntity<Response<CleaningTemplateDto>> cleaningTemplatesTemplateIdPut(
public CleaningTemplateDto cleaningTemplatesTemplateIdPut(
@PathVariable("templateId") String templateId, @RequestBody UpdateCleaningTemplateRequest request) {
return ResponseEntity.ok(Response.ok(cleaningTemplateService.updateTemplate(templateId, request)));
return cleaningTemplateService.updateTemplate(templateId, request);
}
@DeleteMapping("/{templateId}")
public ResponseEntity<Response<Object>> cleaningTemplatesTemplateIdDelete(
public String cleaningTemplatesTemplateIdDelete(
@PathVariable("templateId") String templateId) {
cleaningTemplateService.deleteTemplate(templateId);
return ResponseEntity.noContent().build();
return templateId;
}
}

View File

@@ -6,13 +6,16 @@ import com.datamate.operator.domain.repository.CategoryRepository;
import com.datamate.operator.interfaces.dto.CategoryDto;
import com.datamate.operator.interfaces.dto.CategoryRelationDto;
import com.datamate.operator.interfaces.dto.CategoryTreeResponse;
import com.datamate.operator.interfaces.dto.SubCategory;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors;
@Service
@@ -26,37 +29,40 @@ public class CategoryService {
List<CategoryDto> allCategories = categoryRepo.findAllCategories();
List<CategoryRelationDto> allRelations = categoryRelationRepo.findAllRelation();
Map<Integer, Integer> relationMap = allRelations.stream()
Map<String, Integer> relationMap = allRelations.stream()
.collect(Collectors.groupingBy(
CategoryRelationDto::getCategoryId,
Collectors.collectingAndThen(Collectors.counting(), Math::toIntExact)));
Map<Integer, String> nameMap = allCategories.stream()
.collect(Collectors.toMap(CategoryDto::getId, CategoryDto::getName));
Map<Integer, List<CategoryDto>> groupedByParentId = allCategories.stream()
.filter(relation -> relation.getParentId() > 0)
Map<String, CategoryDto> nameMap = allCategories.stream()
.collect(Collectors.toMap(CategoryDto::getId, Function.identity()));
Map<String, List<CategoryDto>> groupedByParentId = allCategories.stream()
.filter(relation -> !StringUtils.equals(relation.getParentId(), "0"))
.collect(Collectors.groupingBy(CategoryDto::getParentId));
return groupedByParentId.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.sorted(categoryComparator(nameMap))
.map(entry -> {
Integer parentId = entry.getKey();
String parentId = entry.getKey();
List<CategoryDto> group = entry.getValue();
CategoryTreeResponse response = new CategoryTreeResponse();
response.setId(parentId);
response.setName(nameMap.get(parentId));
response.setName(nameMap.get(parentId).getName());
AtomicInteger totalCount = new AtomicInteger();
response.setCategories(group.stream().map(category -> {
SubCategory subCategory = new SubCategory();
subCategory.setId(category.getId());
subCategory.setName(category.getName());
subCategory.setCount(relationMap.getOrDefault(category.getId(), 0));
response.setCategories(group.stream().peek(category -> {
category.setCount(relationMap.getOrDefault(category.getId(), 0));
totalCount.getAndAdd(relationMap.getOrDefault(category.getId(), 0));
subCategory.setParentId(parentId);
return subCategory;
}).toList());
}).sorted(Comparator.comparing(CategoryDto::getCreatedAt)).toList());
response.setCount(totalCount.get());
return response;
}).toList();
}
private Comparator<Map.Entry<String, List<CategoryDto>>> categoryComparator(Map<String, CategoryDto> categoryMap) {
return (entry1, entry2) -> {
LocalDateTime index1 = categoryMap.get(entry1.getKey()).getCreatedAt();
LocalDateTime index2 = categoryMap.get(entry2.getKey()).getCreatedAt();
return index1.compareTo(index2);
};
}
}

View File

@@ -1,21 +0,0 @@
package com.datamate.operator.application;
import com.datamate.operator.interfaces.dto.LabelDto;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Collections;
@Service
public class LabelService {
public List<LabelDto> getLabels(Integer page, Integer size, String keyword) {
// TODO: 查询标签列表
return Collections.emptyList();
}
public void updateLabel(String id, List<LabelDto> updateLabelDtoRequest) {
// TODO: 更新标签
}
public void createLabels(LabelDto labelsPostRequest) {
// TODO: 批量创建标签
}
}

View File

@@ -1,15 +1,22 @@
package com.datamate.operator.application;
import com.datamate.common.domain.model.ChunkUploadPreRequest;
import com.datamate.common.domain.service.FileService;
import com.datamate.operator.domain.contants.OperatorConstant;
import com.datamate.operator.infrastructure.converter.OperatorConverter;
import com.datamate.operator.domain.model.OperatorView;
import com.datamate.operator.domain.repository.CategoryRelationRepository;
import com.datamate.operator.domain.repository.OperatorRepository;
import com.datamate.operator.domain.repository.OperatorViewRepository;
import com.datamate.operator.infrastructure.parser.ParserHolder;
import com.datamate.operator.interfaces.dto.OperatorDto;
import com.datamate.operator.interfaces.dto.UploadOperatorRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.transaction.annotation.Transactional;
import java.io.File;
import java.util.List;
@Service
@@ -21,14 +28,21 @@ public class OperatorService {
private final CategoryRelationRepository relationRepo;
public List<OperatorDto> getOperators(Integer page, Integer size, List<Integer> categories,
private final ParserHolder parserHolder;
private final FileService fileService;
@Value("${operator.base.path:/operator}")
private String operatorBasePath;
public List<OperatorDto> getOperators(Integer page, Integer size, List<String> categories,
String operatorName, Boolean isStar) {
List<OperatorView> filteredOperators = operatorViewRepo.findOperatorsByCriteria(page, size, operatorName,
categories, isStar);
return filteredOperators.stream().map(OperatorConverter.INSTANCE::fromEntityToDto).toList();
}
public int getOperatorsCount(List<Integer> categories, String operatorName, Boolean isStar) {
public int getOperatorsCount(List<String> categories, String operatorName, Boolean isStar) {
return operatorViewRepo.countOperatorsByCriteria(operatorName, categories, isStar);
}
@@ -37,20 +51,60 @@ public class OperatorService {
return OperatorConverter.INSTANCE.fromEntityToDto(operator);
}
@Transactional
public OperatorDto createOperator(OperatorDto req) {
operatorRepo.insertOperator(req);
relationRepo.batchInsert(req.getId(), req.getCategories());
parserHolder.extractTo(getFileType(req.getFileName()), getUploadPath(req.getFileName()),
getExtractPath(getFileNameWithoutExtension(req.getFileName())));
return getOperatorById(req.getId());
}
@Transactional
public OperatorDto updateOperator(String id, OperatorDto req) {
operatorRepo.updateOperator(req);
relationRepo.batchInsert(id, req.getCategories());
parserHolder.extractTo(getFileType(req.getFileName()), getUploadPath(req.getFileName()),
getExtractPath(getFileNameWithoutExtension(req.getFileName())));
return getOperatorById(id);
}
public OperatorDto uploadOperator(MultipartFile file, String description) {
// TODO: 文件上传与解析
return new OperatorDto();
@Transactional
public void deleteOperator(String id) {
operatorRepo.deleteOperator(id);
relationRepo.deleteByOperatorId(id);
}
public OperatorDto uploadOperator(String fileName) {
return parserHolder.parseYamlFromArchive(getFileType(fileName), new File(getUploadPath(fileName)),
OperatorConstant.YAML_PATH);
}
public String preUpload() {
ChunkUploadPreRequest request = ChunkUploadPreRequest.builder().build();
request.setUploadPath(operatorBasePath + File.separator + "upload");
request.setTotalFileNum(1);
request.setServiceId(OperatorConstant.SERVICE_ID);
return fileService.preUpload(request);
}
public void chunkUpload(UploadOperatorRequest request) {
fileService.chunkUpload(OperatorConverter.INSTANCE.toChunkRequest(request));
}
private String getFileType(String fileName) {
return fileName.substring(fileName.lastIndexOf('.') + 1);
}
private String getFileNameWithoutExtension(String fileName) {
return fileName.substring(0, fileName.lastIndexOf('.'));
}
private String getUploadPath(String fileName) {
return operatorBasePath + File.separator + "upload" + File.separator + fileName;
}
private String getExtractPath(String fileName) {
return operatorBasePath + File.separator + "extract" + File.separator + fileName;
}
}

View File

@@ -0,0 +1,42 @@
package com.datamate.operator.domain.contants;
import java.util.HashMap;
import java.util.Map;
public class OperatorConstant {
public static String SERVICE_ID = "operator";
public static String YAML_PATH = "metadata.yml";
public static String CATEGORY_PYTHON = "python";
public static String CATEGORY_PYTHON_ID = "9eda9d5d-072b-499b-916c-797a0a8750e1";
public static String CATEGORY_JAVA = "java";
public static String CATEGORY_JAVA_ID = "b5bfc548-8ef6-417c-b8a6-a4197c078249";
public static String CATEGORY_CUSTOMIZED_ID = "ec2cdd17-8b93-4a81-88c4-ac9e98d10757";
public static String CATEGORY_TEXT_ID = "d8a5df7a-52a9-42c2-83c4-01062e60f597";
public static String CATEGORY_IMAGE_ID = "de36b61c-9e8a-4422-8c31-d30585c7100f";
public static String CATEGORY_AUDIO_ID = "42dd9392-73e4-458c-81ff-41751ada47b5";
public static String CATEGORY_VIDEO_ID = "a233d584-73c8-4188-ad5d-8f7c8dda9c27";
public static String CATEGORY_ALL_ID = "4d7dbd77-0a92-44f3-9056-2cd62d4a71e4";
public static Map<String, String> CATEGORY_MAP = new HashMap<>();
static {
CATEGORY_MAP.put(CATEGORY_PYTHON, CATEGORY_PYTHON_ID);
CATEGORY_MAP.put(CATEGORY_JAVA, CATEGORY_JAVA_ID);
CATEGORY_MAP.put("text", CATEGORY_TEXT_ID);
CATEGORY_MAP.put("image", CATEGORY_IMAGE_ID);
CATEGORY_MAP.put("audio", CATEGORY_AUDIO_ID);
CATEGORY_MAP.put("video", CATEGORY_VIDEO_ID);
CATEGORY_MAP.put("all", CATEGORY_ALL_ID);
}
}

View File

@@ -4,15 +4,21 @@ import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDateTime;
@Setter
@Getter
@TableName(value = "t_operator_category", autoResultMap = true)
public class Category {
private Integer id;
private String id;
private String name;
private String value;
private String type;
private Integer parentId;
private String parentId;
private LocalDateTime createdAt;
}

View File

@@ -10,7 +10,7 @@ import lombok.Setter;
@AllArgsConstructor
@TableName(value = "t_operator_category_relation", autoResultMap = true)
public class CategoryRelation {
private Integer categoryId;
private String categoryId;
private String operatorId;
}

View File

@@ -3,7 +3,6 @@ package com.datamate.operator.domain.repository;
import com.baomidou.mybatisplus.extension.repository.IRepository;
import com.datamate.operator.domain.model.CategoryRelation;
import com.datamate.operator.interfaces.dto.CategoryRelationDto;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@@ -11,5 +10,7 @@ public interface CategoryRelationRepository extends IRepository<CategoryRelation
List<CategoryRelationDto> findAllRelation();
void batchInsert(@Param("operatorId") String operatorId, @Param("categories") List<Integer> categories);
void batchInsert(String operatorId, List<String> categories);
void deleteByOperatorId(String operatorId);
}

View File

@@ -7,9 +7,11 @@ import com.datamate.operator.interfaces.dto.OperatorDto;
import java.util.List;
public interface OperatorRepository extends IRepository<Operator> {
List<Operator> findAllOperators();
List<OperatorDto> findAllOperators();
void updateOperator(OperatorDto operator);
void insertOperator(OperatorDto operator);
void deleteOperator(String id);
}

View File

@@ -7,9 +7,9 @@ import java.util.List;
public interface OperatorViewRepository extends IRepository<OperatorView> {
List<OperatorView> findOperatorsByCriteria(Integer page, Integer size, String operatorName,
List<Integer> categories, Boolean isStar);
List<String> categories, Boolean isStar);
Integer countOperatorsByCriteria(String operatorName, List<Integer> categories, Boolean isStar);
Integer countOperatorsByCriteria(String operatorName, List<String> categories, Boolean isStar);
OperatorView findOperatorById(String id);
}

View File

@@ -1,8 +1,10 @@
package com.datamate.operator.infrastructure.converter;
import com.datamate.common.domain.model.ChunkUploadRequest;
import com.datamate.operator.domain.model.Operator;
import com.datamate.operator.domain.model.OperatorView;
import com.datamate.operator.interfaces.dto.OperatorDto;
import com.datamate.operator.interfaces.dto.UploadOperatorRequest;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Named;
@@ -19,13 +21,17 @@ public interface OperatorConverter {
@Mapping(target = "categories", source = "categories", qualifiedByName = "stringToList")
OperatorDto fromEntityToDto(OperatorView operator);
List<OperatorDto> fromEntityToDto(List<Operator> operator);
@Named("stringToList")
static List<Integer> stringToList(String input) {
static List<String> stringToList(String input) {
if (input == null || input.isEmpty()) {
return Collections.emptyList();
}
return Arrays.stream(input.split(",")).map(Integer::valueOf).toList();
return Arrays.stream(input.split(",")).map(String::valueOf).toList();
}
Operator fromDtoToEntity(OperatorDto operator);
ChunkUploadRequest toChunkRequest(UploadOperatorRequest request);
}

View File

@@ -0,0 +1,21 @@
package com.datamate.operator.infrastructure.exception;
import com.datamate.common.infrastructure.exception.ErrorCode;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum OperatorErrorCode implements ErrorCode {
/**
* 不支持的文件类型
*/
UNSUPPORTED_FILE_TYPE("op.0001", "不支持的文件类型"),
YAML_NOT_FOUND("op.0002", "算子中缺少元数据文件"),
FIELD_NOT_FOUND("op.0003", "缺少必要的字段");
private final String code;
private final String message;
}

View File

@@ -0,0 +1,80 @@
package com.datamate.operator.infrastructure.parser;
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.exception.OperatorErrorCode;
import com.datamate.operator.interfaces.dto.OperatorDto;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.SafeConstructor;
import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
public abstract class AbstractParser {
protected ObjectMapper objectMapper = new ObjectMapper();
protected OperatorDto parseYaml(InputStream yamlContent) {
Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions()));
Map<String, Object> content = yaml.load(yamlContent);
OperatorDto operator = new OperatorDto();
operator.setId(toStringIfNotNull(content.get("raw_id")));
operator.setName(toStringIfNotNull(content.get("name")));
operator.setDescription(toStringIfNotNull(content.get("description")));
operator.setVersion(toStringIfNotNull(content.get("version")));
operator.setInputs(toStringIfNotNull(content.get("inputs")));
operator.setOutputs(toStringIfNotNull(content.get("outputs")));
operator.setRuntime(toJsonIfNotNull(content.get("runtime")));
operator.setSettings(toJsonIfNotNull(content.get("settings")));
List<String> categories = new ArrayList<>();
categories.add(OperatorConstant.CATEGORY_MAP.get(toLowerCaseIfNotNull(content.get("language"))));
categories.add(OperatorConstant.CATEGORY_MAP.get(toLowerCaseIfNotNull(content.get("modal"))));
categories.add(OperatorConstant.CATEGORY_CUSTOMIZED_ID);
operator.setCategories(categories);
return operator;
};
/**
* 从压缩包内读取指定路径的 yaml 文件并解析为指定类型
* @param archive 压缩包路径(zip 或 tar)
* @param entryPath 压缩包内部的文件路径,例如 "config/app.yaml" 或 "./config/app.yaml"
* @return 解析后的对象
*/
public abstract OperatorDto parseYamlFromArchive(File archive, String entryPath);
/**
* 将压缩包解压到目标目录(保持相对路径)
* @param archive 压缩包路径
* @param targetDir 目标目录
*/
public abstract void extractTo(File archive, String targetDir);
private String toStringIfNotNull(Object obj) {
if (obj == null) {
throw BusinessException.of(OperatorErrorCode.FIELD_NOT_FOUND);
}
return obj.toString();
}
private String toLowerCaseIfNotNull(Object obj) {
if (obj == null) {
throw BusinessException.of(OperatorErrorCode.FIELD_NOT_FOUND);
}
return obj.toString().toLowerCase(Locale.ROOT);
}
private String toJsonIfNotNull(Object obj) {
try {
return obj == null ? null : objectMapper.writeValueAsString(obj);
} catch (JsonProcessingException e) {
throw BusinessException.of(SystemErrorCode.UNKNOWN_ERROR, e.getMessage());
}
}
}

View File

@@ -0,0 +1,62 @@
package com.datamate.operator.infrastructure.parser;
import com.datamate.common.infrastructure.exception.BusinessException;
import com.datamate.common.infrastructure.exception.SystemErrorCode;
import com.datamate.operator.infrastructure.exception.OperatorErrorCode;
import com.datamate.operator.interfaces.dto.OperatorDto;
import jakarta.annotation.PostConstruct;
import org.springframework.stereotype.Component;
import java.io.File;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Component
public class ParserHolder {
// 存放 parser:key 为 parser 类型标识(例如 "zip" 或 "tar"),value 为 parser 实例
private final Map<String, AbstractParser> parserMap = new ConcurrentHashMap<>();
// 注册 parser(可在启动时调用)
public void registerParser(String type, AbstractParser parser) {
if (type == null || parser == null) {
throw BusinessException.of(SystemErrorCode.UNKNOWN_ERROR);
}
parserMap.put(type, parser);
}
// 根据类型获取 parser(可能为 null)
public AbstractParser getParser(String type) {
return parserMap.get(type);
}
// 便捷代理:从指定类型的压缩包中读取 entry 并解析为 clazz
public OperatorDto parseYamlFromArchive(String type, File archive, String entryPath) {
AbstractParser parser = getParser(type);
if (parser == null) {
throw BusinessException.of(OperatorErrorCode.UNSUPPORTED_FILE_TYPE,
"No parser registered for type: " + type);
}
return parser.parseYamlFromArchive(archive, entryPath);
}
// 便捷代理:将指定类型的压缩包解压到目标目录
public void extractTo(String type, File archive, String targetDir) {
AbstractParser parser = getParser(type);
if (parser == null) {
throw BusinessException.of(OperatorErrorCode.UNSUPPORTED_FILE_TYPE,
"No parser registered for type: " + type);
}
parser.extractTo(archive, targetDir);
}
public void extractTo(String type, String sourceDir, String targetDir) {
extractTo(type, new File(sourceDir), targetDir);
}
@PostConstruct
public void init() {
// 注册 zip 和 tar parser,key 可根据需要调整(例如 "zip"/"tar")
registerParser("zip", new ZipParser());
registerParser("tar", new TarParser());
}
}

View File

@@ -0,0 +1,77 @@
package com.datamate.operator.infrastructure.parser;
import com.datamate.common.infrastructure.exception.BusinessException;
import com.datamate.common.infrastructure.exception.SystemErrorCode;
import com.datamate.operator.infrastructure.exception.OperatorErrorCode;
import com.datamate.operator.interfaces.dto.OperatorDto;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Objects;
public class TarParser extends AbstractParser {
@Override
public OperatorDto parseYamlFromArchive(File archive, String entryPath) {
// 允许带或不带前导 "./"
String normalized = entryPath.startsWith("./") ? entryPath.substring(2) : entryPath;
try (InputStream fis = Files.newInputStream(archive.toPath());
TarArchiveInputStream tis = new TarArchiveInputStream(fis)) {
TarArchiveEntry entry;
while ((entry = tis.getNextEntry()) != null) {
String name = entry.getName();
if (Objects.equals(name, entryPath) || Objects.equals(name, normalized)) {
// 使用 SnakeYAML 解析当前 entry 的内容到目标类型
return parseYaml(tis);
}
}
} catch (IOException e) {
throw BusinessException.of(SystemErrorCode.FILE_SYSTEM_ERROR, e.getMessage());
}
throw BusinessException.of(OperatorErrorCode.YAML_NOT_FOUND, "Entry not found in tar: " + entryPath);
}
@Override
public void extractTo(File archive, String targetDir) {
Path targetPath = Paths.get(targetDir);
try (InputStream fis = Files.newInputStream(archive.toPath());
TarArchiveInputStream tis = new TarArchiveInputStream(fis)) {
Files.createDirectories(targetPath);
TarArchiveEntry entry;
while ((entry = tis.getNextEntry()) != null) {
String entryName = entry.getName();
// 去掉可能的前导 "./"
if (entryName.startsWith("./")) {
entryName = entryName.substring(2);
}
Path resolved = targetPath.resolve(entryName).toAbsolutePath().normalize();
if (!resolved.startsWith(targetPath.toAbsolutePath().normalize())) {
throw BusinessException.of(SystemErrorCode.FILE_SYSTEM_ERROR, "Bad tar entry: " + entryName);
}
if (entry.isDirectory()) {
Files.createDirectories(resolved);
} else {
Files.createDirectories(resolved.getParent());
try (OutputStream os = Files.newOutputStream(resolved)) {
byte[] buffer = new byte[8192];
int len;
while ((len = tis.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
}
}
}
} catch (IOException e) {
throw BusinessException.of(SystemErrorCode.FILE_SYSTEM_ERROR, e.getMessage());
}
}
}

View File

@@ -0,0 +1,77 @@
package com.datamate.operator.infrastructure.parser;
import com.datamate.common.infrastructure.exception.BusinessException;
import com.datamate.common.infrastructure.exception.SystemErrorCode;
import com.datamate.operator.infrastructure.exception.OperatorErrorCode;
import com.datamate.operator.interfaces.dto.OperatorDto;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
public class ZipParser extends AbstractParser {
@Override
public OperatorDto parseYamlFromArchive(File archive, String entryPath) {
try (ZipFile zipFile = new ZipFile(archive)) {
// 允许带或不带前导 "./"
String normalized = entryPath.startsWith("./") ? entryPath.substring(2) : entryPath;
ZipEntry entry = zipFile.getEntry(entryPath);
if (entry == null) {
entry = zipFile.getEntry(normalized);
}
if (entry == null) {
throw BusinessException.of(OperatorErrorCode.YAML_NOT_FOUND, "Entry not found in zip: " + entryPath);
}
try (InputStream is = zipFile.getInputStream(entry)) {
// 使用 SnakeYAML 解析为目标类型
return parseYaml(is);
}
} catch (IOException e) {
throw BusinessException.of(SystemErrorCode.FILE_SYSTEM_ERROR, e.getMessage());
}
}
@Override
public void extractTo(File archive, String targetDir) {
Path targetPath = Paths.get(targetDir);
try (ZipFile zipFile = new ZipFile(archive)) {
Files.createDirectories(targetPath);
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
String entryName = entry.getName();
// 防止 Zip Slip:确保解压路径仍在 targetDir 下
Path resolved = targetPath.resolve(entryName).toAbsolutePath().normalize();
if (!resolved.startsWith(targetPath.toAbsolutePath().normalize())) {
throw BusinessException.of(SystemErrorCode.FILE_SYSTEM_ERROR, "Bad zip entry: " + entryName);
}
if (entry.isDirectory()) {
Files.createDirectories(resolved);
} else {
Files.createDirectories(resolved.getParent());
try (InputStream is = zipFile.getInputStream(entry);
OutputStream os = Files.newOutputStream(resolved)) {
byte[] buffer = new byte[8192];
int len;
while ((len = is.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
}
}
}
} catch (IOException e) {
throw BusinessException.of(SystemErrorCode.FILE_SYSTEM_ERROR, e.getMessage());
}
}
}

View File

@@ -1,5 +1,6 @@
package com.datamate.operator.infrastructure.persistence.Impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.repository.CrudRepository;
import com.datamate.operator.domain.model.CategoryRelation;
import com.datamate.operator.domain.repository.CategoryRelationRepository;
@@ -23,10 +24,17 @@ public class CategoryRelationRepositoryImpl extends CrudRepository<CategoryRelat
}
@Override
public void batchInsert(String operatorId, List<Integer> categories) {
public void batchInsert(String operatorId, List<String> categories) {
List<CategoryRelation> categoryRelations = categories.stream()
.map(category -> new CategoryRelation(category, operatorId))
.toList();
mapper.insert(categoryRelations);
}
@Override
public void deleteByOperatorId(String operatorId) {
LambdaQueryWrapper<CategoryRelation> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(CategoryRelation::getOperatorId, operatorId);
mapper.delete(queryWrapper);
}
}

View File

@@ -17,8 +17,8 @@ public class OperatorRepositoryImpl extends CrudRepository<OperatorMapper, Opera
private final OperatorMapper mapper;
@Override
public List<Operator> findAllOperators() {
return mapper.selectList(null);
public List<OperatorDto> findAllOperators() {
return OperatorConverter.INSTANCE.fromEntityToDto(mapper.selectList(null));
}
@Override
@@ -30,4 +30,9 @@ public class OperatorRepositoryImpl extends CrudRepository<OperatorMapper, Opera
public void insertOperator(OperatorDto operator) {
mapper.insert(OperatorConverter.INSTANCE.fromDtoToEntity(operator));
}
@Override
public void deleteOperator(String id) {
mapper.deleteById(id);
}
}

View File

@@ -22,7 +22,7 @@ public class OperatorViewRepositoryImpl extends CrudRepository<OperatorViewMappe
@Override
public List<OperatorView> findOperatorsByCriteria(Integer page, Integer size, String operatorName,
List<Integer> categories, Boolean isStar) {
List<String> categories, Boolean isStar) {
QueryWrapper<OperatorView> queryWrapper = Wrappers.query();
queryWrapper.in(CollectionUtils.isNotEmpty(categories), "category_id", categories)
.like(StringUtils.isNotBlank(operatorName), "operator_name", operatorName)
@@ -37,7 +37,7 @@ public class OperatorViewRepositoryImpl extends CrudRepository<OperatorViewMappe
}
@Override
public Integer countOperatorsByCriteria(String operatorName, List<Integer> categories, Boolean isStar) {
public Integer countOperatorsByCriteria(String operatorName, List<String> categories, Boolean isStar) {
QueryWrapper<OperatorView> queryWrapper = Wrappers.query();
queryWrapper.in(CollectionUtils.isNotEmpty(categories),"category_id", categories)
.like(StringUtils.isNotBlank(operatorName), "operator_name", operatorName)

View File

@@ -3,14 +3,22 @@ package com.datamate.operator.interfaces.dto;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDateTime;
@Setter
@Getter
public class CategoryDto {
private Integer id;
private String id;
private String name;
private String value;
private long count;
private String type;
private Integer parentId;
private String parentId;
private LocalDateTime createdAt;
}

View File

@@ -6,7 +6,7 @@ import lombok.Setter;
@Setter
@Getter
public class CategoryRelationDto {
private Integer categoryId;
private String categoryId;
private String operatorId;
}

View File

@@ -12,11 +12,11 @@ import java.util.List;
@Setter
@NoArgsConstructor
public class CategoryTreeResponse {
private Integer id;
private String id;
private String name;
private Integer count;
private List<SubCategory> categories = new ArrayList<>();
private List<CategoryDto> categories = new ArrayList<>();
}

View File

@@ -14,32 +14,32 @@ import java.util.List;
@Getter
@Setter
public class OperatorDto {
private String id;
private String id;
private String name;
private String name;
private String description;
private String description;
private String version;
private String version;
private String inputs;
private String inputs;
private String outputs;
private String outputs;
private List<Integer> categories;
private List<String> categories;
private String runtime;
private String runtime;
private String settings;
private String settings;
private String fileName;
private String fileName;
private Boolean isStar;
private Boolean isStar;
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
private LocalDateTime createdAt;
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
private LocalDateTime createdAt;
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
private LocalDateTime updatedAt;
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
private LocalDateTime updatedAt;
}

View File

@@ -19,7 +19,7 @@ public class OperatorsListPostRequest {
private Integer size;
private List<Integer> categories = new ArrayList<>();
private List<String> categories = new ArrayList<>();
private String operatorName;

View File

@@ -1,18 +0,0 @@
package com.datamate.operator.interfaces.dto;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class SubCategory {
private long id;
private String name;
private long count;
private String type;
private long parentId;
}

View File

@@ -0,0 +1,34 @@
package com.datamate.operator.interfaces.dto;
import lombok.Getter;
import lombok.Setter;
import org.springframework.web.multipart.MultipartFile;
/**
* 上传文件请求
* 用于分块上传文件时的请求参数封装,支持大文件分片上传功能
*/
@Getter
@Setter
public class UploadOperatorRequest {
/** 预上传返回的id,用来确认同一个任务 */
private String reqId;
/** 文件编号,用于标识批量上传中的第几个文件 */
private int fileNo;
/** 文件名称 */
private String fileName;
/** 文件总分块数量 */
private int totalChunkNum;
/** 当前分块编号,从1开始 */
private int chunkNo;
/** 上传的文件分块内容 */
private MultipartFile file;
/** 文件分块的校验和(十六进制字符串),用于验证文件完整性 */
private String checkSumHex;
}

View File

@@ -1,11 +1,9 @@
package com.datamate.operator.interfaces.rest;
import com.datamate.common.infrastructure.common.Response;
import com.datamate.common.interfaces.PagedResponse;
import com.datamate.operator.application.CategoryService;
import com.datamate.operator.interfaces.dto.CategoryTreeResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@@ -20,8 +18,8 @@ public class CategoryController {
private final CategoryService categoryService;
@GetMapping("/tree")
public ResponseEntity<Response<PagedResponse<CategoryTreeResponse>>> categoryTreeGet() {
public PagedResponse<CategoryTreeResponse> categoryTreeGet() {
List<CategoryTreeResponse> allCategories = categoryService.getAllCategories();
return ResponseEntity.ok(Response.ok(PagedResponse.of(allCategories)));
return PagedResponse.of(allCategories);
}
}

View File

@@ -1,40 +0,0 @@
package com.datamate.operator.interfaces.rest;
import com.datamate.common.infrastructure.common.Response;
import com.datamate.common.interfaces.PagedResponse;
import com.datamate.operator.application.LabelService;
import com.datamate.operator.interfaces.dto.LabelDto;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/labels")
@RequiredArgsConstructor
public class LabelController {
private final LabelService labelService;
@GetMapping
public ResponseEntity<Response<PagedResponse<LabelDto>>> labelsGet(@RequestParam("page") Integer page,
@RequestParam("size") Integer size,
@RequestParam("keyword") String keyword) {
return ResponseEntity.ok(Response.ok(PagedResponse.of(labelService.getLabels(page, size, keyword))));
}
@PutMapping("/{id}")
public ResponseEntity<Response<Object>> labelsIdPut(@PathVariable("id") String id,
@RequestBody List<LabelDto> updateLabelDtoRequest) {
labelService.updateLabel(id, updateLabelDtoRequest);
return ResponseEntity.ok(Response.ok(null));
}
@PostMapping
public ResponseEntity<Response<Object>> labelsPost(@RequestBody LabelDto labelsPostRequest) {
labelService.createLabels(labelsPostRequest);
return ResponseEntity.ok(Response.ok(null));
}
}

View File

@@ -1,14 +1,13 @@
package com.datamate.operator.interfaces.rest;
import com.datamate.common.infrastructure.common.Response;
import com.datamate.common.interfaces.PagedResponse;
import com.datamate.operator.application.OperatorService;
import com.datamate.operator.interfaces.dto.OperatorDto;
import com.datamate.operator.interfaces.dto.OperatorsListPostRequest;
import com.datamate.operator.interfaces.dto.UploadOperatorRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
@@ -19,34 +18,48 @@ public class OperatorController {
private final OperatorService operatorService;
@PostMapping("/list")
public ResponseEntity<Response<PagedResponse<OperatorDto>>> operatorsListPost(@RequestBody OperatorsListPostRequest request) {
public PagedResponse<OperatorDto> operatorsListPost(@RequestBody OperatorsListPostRequest request) {
List<OperatorDto> responses = operatorService.getOperators(request.getPage(), request.getSize(),
request.getCategories(), request.getOperatorName(), request.getIsStar());
int count = operatorService.getOperatorsCount(request.getCategories(), request.getOperatorName(),
request.getIsStar());
int totalPages = (count + request.getSize() + 1) / request.getSize();
return ResponseEntity.ok(Response.ok(PagedResponse.of(responses, request.getPage(), count, totalPages)));
return PagedResponse.of(responses, request.getPage(), count, totalPages);
}
@GetMapping("/{id}")
public ResponseEntity<Response<OperatorDto>> operatorsIdGet(@PathVariable("id") String id) {
return ResponseEntity.ok(Response.ok(operatorService.getOperatorById(id)));
public OperatorDto operatorsIdGet(@PathVariable("id") String id) {
return operatorService.getOperatorById(id);
}
@PutMapping("/{id}")
public ResponseEntity<Response<OperatorDto>> operatorsIdPut(@PathVariable("id") String id,
public OperatorDto operatorsIdPut(@PathVariable("id") String id,
@RequestBody OperatorDto updateOperatorRequest) {
return ResponseEntity.ok(Response.ok(operatorService.updateOperator(id, updateOperatorRequest)));
return operatorService.updateOperator(id, updateOperatorRequest);
}
@PostMapping("/create")
public ResponseEntity<Response<OperatorDto>> operatorsCreatePost(@RequestBody OperatorDto createOperatorRequest) {
return ResponseEntity.ok(Response.ok(operatorService.createOperator(createOperatorRequest)));
public OperatorDto operatorsCreatePost(@RequestBody OperatorDto createOperatorRequest) {
return operatorService.createOperator(createOperatorRequest);
}
@PostMapping("/upload")
public ResponseEntity<Response<OperatorDto>> operatorsUploadPost(@RequestPart(value = "file") MultipartFile file,
@RequestParam(value = "description") String description) {
return ResponseEntity.ok(Response.ok(operatorService.uploadOperator(file, description)));
public OperatorDto operatorsUploadPost(@RequestBody UploadOperatorRequest request) {
return operatorService.uploadOperator(request.getFileName());
}
@PostMapping(value = "/upload/pre-upload", produces = MediaType.APPLICATION_JSON_VALUE)
public String preUpload() {
return operatorService.preUpload();
}
@PostMapping("/upload/chunk")
public void chunkUpload(@ModelAttribute UploadOperatorRequest request) {
operatorService.chunkUpload(request);
}
@DeleteMapping("/{id}")
public void operatorDelete(@PathVariable("id") String id) {
operatorService.deleteOperator(id);
}
}

View File

@@ -9,6 +9,8 @@ services:
- dataset_volume:/dataset
- flow_volume:/flow
- log_volume:/var/log/datamate
- operator-upload-volume:/operators/upload
- operator-runtime-volume:/operators/extract
networks: [ datamate ]
depends_on:
- datamate-database
@@ -71,6 +73,7 @@ services:
- log_volume:/var/log/datamate
- dataset_volume:/dataset
- flow_volume:/flow
- operator-runtime-volume:/opt/runtime/datamate/ops/user
networks: [ datamate ]
# 4) mineru
@@ -109,6 +112,10 @@ volumes:
name: datamate-frontend-log-volume
database_log_volume:
name: datamate-database-log-volume
operator-upload-volume:
name: datamate-operator-upload-volume
operator-runtime-volume:
name: datamate-operator-runtime-volume
networks:
datamate:

View File

@@ -0,0 +1,28 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: datamate-nginx-conf
data:
backend.conf: |
server {
listen 80;
server_name 0.0.0.0;
access_log /var/log/datamate/frontend/access.log main;
error_log /var/log/datamate/frontend/error.log notice;
client_max_body_size 1024M;
location /api/ {
proxy_pass http://datamate-backend:8080/api/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location / {
root /opt/frontend;
try_files $uri $uri/ /index.html;
}
}

View File

@@ -113,63 +113,11 @@ head:
securityContext: {}
# Optional: The following volumes/volumeMounts configurations are optional but recommended because
# Ray writes logs to /tmp/ray/session_latests/logs instead of stdout/stderr.
volumes:
- name: log-volume
hostPath:
path: /opt/datamate/data/log
type: DirectoryOrCreate
- name: dataset-volume
hostPath:
path: /opt/datamate/data/dataset
type: DirectoryOrCreate
- name: flow-volume
hostPath:
path: /opt/datamate/data/flow
type: DirectoryOrCreate
volumeMounts:
- mountPath: /tmp/ray
name: log-volume
subPath: ray/head
- mountPath: /dataset
name: dataset-volume
- mountPath: /flow
name: flow-volume
volumes: []
volumeMounts: []
# sidecarContainers specifies additional containers to attach to the Ray pod.
# Follows standard K8s container spec.
sidecarContainers:
- name: runtime
image: datamate-runtime
imagePullPolicy: IfNotPresent
command:
- python
- /opt/runtime/datamate/operator_runtime.py
- --port
- "8081"
env:
- name: MYSQL_HOST
value: "datamate-database"
- name: MYSQL_PORT
value: "3306"
- name: MYSQL_USER
value: "root"
- name: MYSQL_PASSWORD
value: "password"
- name: MYSQL_DATABASE
value: "datamate"
- name: PDF_FORMATTER_BASE_URL
value: "http://datamate-mineru:9001"
ports:
- containerPort: 8081
volumeMounts:
- mountPath: /tmp/ray
name: log-volume
subPath: ray/head
- mountPath: /var/log/datamate
name: log-volume
- mountPath: /dataset
name: dataset-volume
- mountPath: /flow
name: flow-volume
sidecarContainers: []
# See docs/guidance/pod-command.md for more details about how to specify
# container command for head Pod.
command: []
@@ -260,27 +208,8 @@ worker:
securityContext: {}
# Optional: The following volumes/volumeMounts configurations are optional but recommended because
# Ray writes logs to /tmp/ray/session_latests/logs instead of stdout/stderr.
volumes:
- name: log-volume
hostPath:
path: /opt/datamate/data/log
type: DirectoryOrCreate
- name: dataset-volume
hostPath:
path: /opt/datamate/data/dataset
type: DirectoryOrCreate
- name: flow-volume
hostPath:
path: /opt/datamate/data/flow
type: DirectoryOrCreate
volumeMounts:
- mountPath: /tmp/ray
name: log-volume
subPath: ray/worker
- mountPath: /dataset
name: dataset-volume
- mountPath: /flow
name: flow-volume
volumes: []
volumeMounts: []
# sidecarContainers specifies additional containers to attach to the Ray pod.
# Follows standard K8s container spec.
sidecarContainers: []

View File

@@ -40,11 +40,17 @@ dataVolume: &dataVolume
path: /opt/datamate/data/mysql
type: DirectoryOrCreate
operatorVolume: &operatorVolume
name: operator-volume
hostPath:
path: /opt/datamate/data/operator
backend:
volumes:
- *datasetVolume
- *flowVolume
- *logVolume
- *operatorVolume
volumeMounts:
- name: dataset-volume
mountPath: /dataset
@@ -52,14 +58,22 @@ backend:
mountPath: /flow
- name: log-volume
mountPath: /var/log/datamate
- name: operator-volume
mountPath: /operators
frontend:
volumes:
- *logVolume
- name: datamate-nginx-conf
configMap:
name: datamate-nginx-conf
volumeMounts:
- name: log-volume
mountPath: /var/log/datamate/frontend
subPath: frontend
- mountPath: /etc/nginx/conf.d/backend.conf
name: datamate-nginx-conf
subPath: backend.conf
database:
volumes:
@@ -81,3 +95,76 @@ database:
mountPath: /docker-entrypoint-initdb.d
- name: mysql-utf8-config
mountPath: /etc/mysql/conf.d
ray-cluster:
head:
volumes:
- *datasetVolume
- *flowVolume
- *logVolume
- *operatorVolume
volumeMounts:
- mountPath: /tmp/ray
name: log-volume
subPath: ray/head
- mountPath: /dataset
name: dataset-volume
- mountPath: /flow
name: flow-volume
- mountPath: /opt/runtime/datamate/ops/user
name: operator-volume
subPath: extract
sidecarContainers:
- name: runtime
image: datamate-runtime
imagePullPolicy: IfNotPresent
args:
- python
- /opt/runtime/datamate/operator_runtime.py
- --port
- "8081"
env:
- name: MYSQL_HOST
value: "datamate-database"
- name: MYSQL_PORT
value: "3306"
- name: MYSQL_USER
value: "root"
- name: MYSQL_PASSWORD
value: "password"
- name: MYSQL_DATABASE
value: "datamate"
- name: PDF_FORMATTER_BASE_URL
value: "http://datamate-mineru:9001"
ports:
- containerPort: 8081
volumeMounts:
- mountPath: /tmp/ray
name: log-volume
subPath: ray/head
- mountPath: /var/log/datamate
name: log-volume
- mountPath: /dataset
name: dataset-volume
- mountPath: /flow
name: flow-volume
- mountPath: /opt/runtime/datamate/ops/user
name: operator-volume
subPath: extract
worker:
volumes:
- *datasetVolume
- *flowVolume
- *logVolume
- *operatorVolume
volumeMounts:
- mountPath: /tmp/ray
name: log-volume
subPath: ray/worker
- mountPath: /dataset
name: dataset-volume
- mountPath: /flow
name: flow-volume
- mountPath: /opt/runtime/datamate/ops/user
name: operator-volume
subPath: extract

View File

@@ -3,7 +3,7 @@ USE datamate;
CREATE TABLE IF NOT EXISTS t_clean_template
(
id varchar(64) primary key not null unique,
name varchar(64),
name varchar(64) unique,
description varchar(256),
created_at timestamp default current_timestamp,
updated_at timestamp default current_timestamp,
@@ -13,7 +13,7 @@ CREATE TABLE IF NOT EXISTS t_clean_template
CREATE TABLE IF NOT EXISTS t_clean_task
(
id varchar(64) primary key,
name varchar(64),
name varchar(64) unique,
description varchar(256),
status varchar(256),
src_dataset_id varchar(64),

View File

@@ -3,7 +3,7 @@ USE datamate;
CREATE TABLE IF NOT EXISTS t_operator
(
id varchar(64) primary key,
name varchar(64),
name varchar(64) unique,
description varchar(256),
version varchar(256),
inputs varchar(256),
@@ -18,15 +18,17 @@ CREATE TABLE IF NOT EXISTS t_operator
CREATE TABLE IF NOT EXISTS t_operator_category
(
id int primary key auto_increment,
name varchar(64),
id varchar(64) primary key,
name varchar(64) unique ,
value varchar(64) unique ,
type varchar(64),
parent_id int
parent_id varchar(64),
created_at timestamp default current_timestamp
);
CREATE TABLE IF NOT EXISTS t_operator_category_relation
(
category_id int,
category_id varchar(64),
operator_id varchar(64),
primary key (category_id, operator_id)
);
@@ -41,7 +43,7 @@ SELECT o.id AS operator_id,
runtime,
settings,
is_star,
created_at,
o.created_at AS created_at,
updated_at,
toc.id AS category_id,
toc.name AS category_name
@@ -49,21 +51,21 @@ FROM t_operator_category_relation tocr
LEFT JOIN t_operator o ON tocr.operator_id = o.id
LEFT JOIN t_operator_category toc ON tocr.category_id = toc.id;
INSERT IGNORE INTO t_operator_category(id, name, type, parent_id)
VALUES (1, '模态', 'predefined', 0),
(2, '语言', 'predefined', 0),
(3, '文本', 'predefined', 1),
(4, '图片', 'predefined', 1),
(5, '音频', 'predefined', 1),
(6, '视频', 'predefined', 1),
(7, '多模态', 'predefined', 1),
(8, 'Python', 'predefined', 2),
(9, 'Java', 'predefined', 2),
(10, '来源', 'predefined', 0),
(11, '系统预置', 'predefined', 10),
(12, '用户上传', 'predefined', 10),
(13, '收藏状态', 'predefined', 0),
(14, '已收藏', 'predefined', 13);
INSERT IGNORE INTO t_operator_category(id, name, value, type, parent_id)
VALUES ('64465bec-b46b-11f0-8291-00155d0e4808', '模态', 'modal', 'predefined', '0'),
('873000a2-65b3-474b-8ccc-4813c08c76fb', '语言', 'language', 'predefined', '0'),
('d8a5df7a-52a9-42c2-83c4-01062e60f597', '文本', 'text', 'predefined', '64465bec-b46b-11f0-8291-00155d0e4808'),
('de36b61c-9e8a-4422-8c31-d30585c7100f', '图片', 'image', 'predefined', '64465bec-b46b-11f0-8291-00155d0e4808'),
('42dd9392-73e4-458c-81ff-41751ada47b5', '音频', 'audio', 'predefined', '64465bec-b46b-11f0-8291-00155d0e4808'),
('a233d584-73c8-4188-ad5d-8f7c8dda9c27', '视频', 'video', 'predefined', '64465bec-b46b-11f0-8291-00155d0e4808'),
('4d7dbd77-0a92-44f3-9056-2cd62d4a71e4', '多模态', 'multimodal', 'predefined', '64465bec-b46b-11f0-8291-00155d0e4808'),
('9eda9d5d-072b-499b-916c-797a0a8750e1', 'Python', 'python', 'predefined', '873000a2-65b3-474b-8ccc-4813c08c76fb'),
('b5bfc548-8ef6-417c-b8a6-a4197c078249', 'Java', 'java', 'predefined', '873000a2-65b3-474b-8ccc-4813c08c76fb'),
('16e2d99e-eafb-44fc-acd0-f35a2bad28f8', '来源', 'origin', 'predefined', '0'),
('96a3b07a-3439-4557-a835-525faad60ca3', '系统预置', 'predefined', 'predefined', '16e2d99e-eafb-44fc-acd0-f35a2bad28f8'),
('ec2cdd17-8b93-4a81-88c4-ac9e98d10757', '用户上传', 'customized', 'predefined', '16e2d99e-eafb-44fc-acd0-f35a2bad28f8'),
('d8482257-7ee6-41a0-a914-8363c7db1db0', '收藏状态', 'starStatus', 'predefined', '0'),
('79f2d35a-3b6c-4846-a892-2f2015f48f24', '已收藏', 'isStar', 'predefined', 'd8482257-7ee6-41a0-a914-8363c7db1db0');
INSERT IGNORE INTO t_operator
(id, name, description, version, inputs, outputs, runtime, settings, file_name, is_star)
@@ -116,7 +118,7 @@ INSERT IGNORE INTO t_operator_category_relation(category_id, operator_id)
SELECT c.id, o.id
FROM t_operator_category c
CROSS JOIN t_operator o
WHERE c.id IN (3, 8, 11)
WHERE c.id IN ('d8a5df7a-52a9-42c2-83c4-01062e60f597', '9eda9d5d-072b-499b-916c-797a0a8750e1', '96a3b07a-3439-4557-a835-525faad60ca3')
AND o.id IN ('TextFormatter', 'FileWithShortOrLongLengthFilter', 'FileWithHighRepeatPhraseRateFilter',
'FileWithHighRepeatWordRateFilter', 'FileWithHighSpecialCharRateFilter', 'FileWithManySensitiveWordsFilter',
'DuplicateFilesFilter', 'DuplicateSentencesFilter', 'AnonymizedCreditCardNumber', 'AnonymizedIdNumber',
@@ -129,7 +131,7 @@ INSERT IGNORE INTO t_operator_category_relation(category_id, operator_id)
SELECT c.id, o.id
FROM t_operator_category c
CROSS JOIN t_operator o
WHERE c.id IN (4, 8, 11)
WHERE c.id IN ('de36b61c-9e8a-4422-8c31-d30585c7100f', '9eda9d5d-072b-499b-916c-797a0a8750e1', '96a3b07a-3439-4557-a835-525faad60ca3')
AND o.id IN ('ImgFormatter', 'ImgBlurredImagesCleaner', 'ImgBrightness', 'ImgContrast', 'ImgDenoise',
'ImgDuplicatedImagesCleaner', 'ImgPerspectiveTransformation', 'ImgResize', 'ImgSaturation',
'ImgShadowRemove', 'ImgSharpness', 'ImgSimilarImagesCleaner', 'ImgTypeUnify');
@@ -138,5 +140,5 @@ INSERT IGNORE INTO t_operator_category_relation(category_id, operator_id)
SELECT c.id, o.id
FROM t_operator_category c
CROSS JOIN t_operator o
WHERE c.id IN (7, 8, 11)
AND o.id IN ('FileExporter', 'UnstructuredFormatter', 'ExternalPDFFormatter');
WHERE c.id IN ('4d7dbd77-0a92-44f3-9056-2cd62d4a71e4', '9eda9d5d-072b-499b-916c-797a0a8750e1', '96a3b07a-3439-4557-a835-525faad60ca3')
AND o.id IN ('FileExporter', 'UnstructuredFormatter');

View File

@@ -14,10 +14,9 @@ RUN cd DataX && \
FROM maven:3-amazoncorretto-21-debian AS builder
COPY backend/ /opt/backend
COPY scripts/images/backend/settings.xml /opt/backend
RUN cd /opt/backend && \
mvn -U clean package -s settings.xml -Dmaven.test.skip=true
mvn -U clean package -Dmaven.test.skip=true
FROM openjdk:21-jdk-slim
@@ -25,7 +24,7 @@ FROM openjdk:21-jdk-slim
RUN apt-get update && \
apt-get install -y vim wget curl nfs-common rsync python3 python3-pip python-is-python3 dos2unix && \
apt-get clean && \
rm -rf /var/lib/apy/lists/*
rm -rf /var/lib/apt/lists/*
COPY --from=builder /opt/backend/services/main-application/target/data-mate.jar /opt/backend/data-mate.jar
COPY --from=datax-builder /DataX/target/datax/datax /opt/datax

View File

@@ -1,68 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 https://maven.apache.org/xsd/settings-1.0.0.xsd">
<!-- 本地仓库路径(可选,默认在 ~/.m2/repository) -->
<localRepository>${user.home}/.m2/repository</localRepository>
<!-- 阿里云镜像配置 -->
<mirrors>
<mirror>
<id>aliyun-maven</id>
<name>Aliyun Maven Repository</name>
<url>https://maven.aliyun.com/repository/public</url>
<mirrorOf>central,jcenter,google,spring,spring-plugin,gradle-plugin</mirrorOf>
</mirror>
</mirrors>
<!-- 使用 Java 21 编译配置(可选,但推荐) -->
<profiles>
<profile>
<id>java21</id>
<activation>
<activeByDefault>true</activeByDefault>
<jdk>21</jdk>
</activation>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</profile>
<!-- 激活阿里云仓库(可选,增强依赖解析) -->
<profile>
<id>aliyun-repos</id>
<repositories>
<repository>
<id>aliyun-public</id>
<name>Aliyun Public Repository</name>
<url>https://maven.aliyun.com/repository/public</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled> <!-- 默认关闭快照版本 -->
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>aliyun-plugin</id>
<name>Aliyun Plugin Repository</name>
<url>https://maven.aliyun.com/repository/public</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</profile>
</profiles>
<activeProfiles>
<activeProfile>aliyun-repos</activeProfile> <!-- 激活阿里云仓库 -->
<activeProfile>java21</activeProfile> <!-- 激活 Java 21 配置 -->
</activeProfiles>
</settings>

View File

@@ -2,6 +2,8 @@ FROM python:3.11
COPY runtime/python-executor /opt/runtime
COPY runtime/ops /opt/runtime/datamate/ops
COPY runtime/ops/user /opt/runtime/user
COPY scripts/images/runtime/start.sh /opt/runtime/start.sh
ENV PYTHONPATH=/opt/runtime/datamate/
@@ -12,12 +14,13 @@ RUN apt update \
WORKDIR /opt/runtime
ENV HF_HUB_DISABLE_XET=1
RUN pip install -e . \
&& pip install -r /opt/runtime/datamate/ops/requirements.txt \
RUN pip install -e . --trusted-host mirrors.huaweicloud.com -i https://mirrors.huaweicloud.com/repository/pypi/simple \
&& pip install -r /opt/runtime/datamate/ops/requirements.txt --trusted-host mirrors.huaweicloud.com -i https://mirrors.huaweicloud.com/repository/pypi/simple \
&& pip cache purge
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& chmod +x /opt/runtime/start.sh
EXPOSE 8081
ENTRYPOINT ["/opt/runtime/start.sh"]

View File

@@ -0,0 +1,8 @@
#!/bin/bash
set -e
cp -r /opt/runtime/user/* /opt/runtime/datamate/ops/user
echo "Starting main application..."
exec "$@"