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; \ git clone git@github.com:bytedance/deer-flow.git ../deer-flow; \
fi fi
sed -i "s/dark/light/g" "../deer-flow/web/src/components/deer-flow/theme-provider-wrapper.tsx" 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 -n 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/conf.yaml.example ../deer-flow/conf.yaml
cd ../deer-flow && docker compose build cd ../deer-flow && docker compose build
.PHONY: mineru-docker-build .PHONY: mineru-docker-build
@@ -131,16 +131,16 @@ mineru-k8s-uninstall:
.PHONY: datamate-docker-install .PHONY: datamate-docker-install
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 .PHONY: datamate-docker-uninstall
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 .PHONY: deer-flow-docker-install
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/datamate && cp -n .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/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 .PHONY: deer-flow-docker-uninstall
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> <artifactId>data-management-service</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>com.datamate</groupId>
<artifactId>operator-market-service</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId> <artifactId>spring-boot-starter-test</artifactId>

View File

@@ -1,6 +1,6 @@
package com.datamate.cleaning; 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.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.EnableScheduling;
@@ -8,9 +8,11 @@ import org.springframework.scheduling.annotation.EnableScheduling;
* 数据归集服务配置类 * 数据归集服务配置类
* 基于DataX的数据归集和同步服务,支持多种数据源的数据采集和归集 * 基于DataX的数据归集和同步服务,支持多种数据源的数据采集和归集
*/ */
@SpringBootApplication
@EnableAsync @EnableAsync
@EnableScheduling @EnableScheduling
@ComponentScan(basePackages = {
"com.datamate.cleaning"
})
public class DataCleaningServiceConfiguration { public class DataCleaningServiceConfiguration {
// Configuration class for JAR packaging - no main method needed // 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.CleaningTaskRepository;
import com.datamate.cleaning.domain.repository.OperatorInstanceRepository; 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.CleaningProcess;
import com.datamate.cleaning.interfaces.dto.CleaningTaskDto; import com.datamate.cleaning.interfaces.dto.CleaningTaskDto;
import com.datamate.cleaning.interfaces.dto.CreateCleaningTaskRequest; import com.datamate.cleaning.interfaces.dto.CreateCleaningTaskRequest;
@@ -59,6 +60,8 @@ public class CleaningTaskService {
private final DatasetFileApplicationService datasetFileService; private final DatasetFileApplicationService datasetFileService;
private final CleanTaskValidator cleanTaskValidator;
private final String DATASET_PATH = "/dataset"; private final String DATASET_PATH = "/dataset";
private final String FLOW_PATH = "/flow"; private final String FLOW_PATH = "/flow";
@@ -80,6 +83,9 @@ public class CleaningTaskService {
@Transactional @Transactional
public CleaningTaskDto createTask(CreateCleaningTaskRequest request) { public CleaningTaskDto createTask(CreateCleaningTaskRequest request) {
cleanTaskValidator.checkNameDuplication(request.getName());
cleanTaskValidator.checkInputAndOutput(request.getInstance());
CreateDatasetRequest createDatasetRequest = new CreateDatasetRequest(); CreateDatasetRequest createDatasetRequest = new CreateDatasetRequest();
createDatasetRequest.setName(request.getDestDatasetName()); createDatasetRequest.setName(request.getDestDatasetName());
createDatasetRequest.setDatasetType(DatasetType.valueOf(request.getDestDatasetType())); 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.domain.repository.OperatorInstanceRepository;
import com.datamate.cleaning.interfaces.dto.*; import com.datamate.cleaning.interfaces.dto.*;
import com.datamate.cleaning.domain.model.entity.TemplateWithInstance; 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 lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@@ -24,8 +26,10 @@ public class CleaningTemplateService {
private final OperatorInstanceRepository operatorInstanceRepo; private final OperatorInstanceRepository operatorInstanceRepo;
private final OperatorRepository operatorRepo;
public List<CleaningTemplateDto> getTemplates(String keywords) { public List<CleaningTemplateDto> getTemplates(String keywords) {
List<OperatorDto> allOperators = operatorInstanceRepo.findAllOperators(); List<OperatorDto> allOperators = operatorRepo.findAllOperators();
Map<String, OperatorDto> operatorsMap = allOperators.stream() Map<String, OperatorDto> operatorsMap = allOperators.stream()
.collect(Collectors.toMap(OperatorDto::getId, Function.identity())); .collect(Collectors.toMap(OperatorDto::getId, Function.identity()));
List<TemplateWithInstance> allTemplates = cleaningTemplateRepo.findAllTemplates(keywords); List<TemplateWithInstance> allTemplates = cleaningTemplateRepo.findAllTemplates(keywords);

View File

@@ -12,7 +12,7 @@ public enum CleanErrorCode implements ErrorCode {
*/ */
DUPLICATE_TASK_NAME("clean.0001", "清洗任务名称重复"), DUPLICATE_TASK_NAME("clean.0001", "清洗任务名称重复"),
CREATE_DATASET_FAILED("clean.0002", "创建数据集失败"); IN_AND_OUT_NOT_MATCH("clean.0002", "算子输入输出不匹配");
private final String code; private final String code;
private final String message; 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 updateTask(CleaningTaskDto task);
void deleteTaskById(String taskId); void deleteTaskById(String taskId);
boolean isNameExist(String name);
} }

View File

@@ -1,15 +1,12 @@
package com.datamate.cleaning.domain.repository; package com.datamate.cleaning.domain.repository;
import com.baomidou.mybatisplus.extension.repository.IRepository; 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.interfaces.dto.OperatorInstanceDto;
import com.datamate.cleaning.domain.model.entity.OperatorInstance; import com.datamate.cleaning.domain.model.entity.OperatorInstance;
import java.util.List; import java.util.List;
public interface OperatorInstanceRepository extends IRepository<OperatorInstance> { public interface OperatorInstanceRepository extends IRepository<OperatorInstance> {
List<OperatorDto> findAllOperators();
void insertInstance(String instanceId, List<OperatorInstanceDto> instances); void insertInstance(String instanceId, List<OperatorInstanceDto> instances);
void deleteByInstanceId(String instanceId); 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.OperatorInstance;
import com.datamate.cleaning.domain.model.entity.Operator; 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.cleaning.interfaces.dto.OperatorInstanceDto;
import com.datamate.common.infrastructure.exception.BusinessException; import com.datamate.common.infrastructure.exception.BusinessException;
import com.datamate.common.infrastructure.exception.SystemErrorCode; import com.datamate.common.infrastructure.exception.SystemErrorCode;
import com.datamate.operator.interfaces.dto.OperatorDto;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import org.mapstruct.Mapper; import org.mapstruct.Mapper;

View File

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

View File

@@ -1,17 +1,10 @@
package com.datamate.cleaning.infrastructure.persistence.mapper; package com.datamate.cleaning.infrastructure.persistence.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.datamate.cleaning.domain.model.entity.Operator;
import com.datamate.cleaning.domain.model.entity.OperatorInstance; import com.datamate.cleaning.domain.model.entity.OperatorInstance;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper @Mapper
public interface OperatorInstanceMapper extends BaseMapper<OperatorInstance> { 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.time.LocalDateTime;
import java.util.List; import java.util.List;
import com.datamate.operator.interfaces.dto.OperatorDto;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import org.springframework.format.annotation.DateTimeFormat; import org.springframework.format.annotation.DateTimeFormat;

View File

@@ -4,6 +4,7 @@ import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import com.datamate.operator.interfaces.dto.OperatorDto;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import org.springframework.format.annotation.DateTimeFormat; 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; package com.datamate.cleaning.interfaces.dto;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
@@ -17,6 +18,14 @@ public class OperatorInstanceDto {
private String id; private String id;
private String name;
private String inputs;
private String outputs;
private List<Integer> categories;
private Map<String, Object> overrides = new HashMap<>(); 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.application.CleaningTaskService;
import com.datamate.cleaning.interfaces.dto.CleaningTaskDto; import com.datamate.cleaning.interfaces.dto.CleaningTaskDto;
import com.datamate.cleaning.interfaces.dto.CreateCleaningTaskRequest; import com.datamate.cleaning.interfaces.dto.CreateCleaningTaskRequest;
import com.datamate.common.infrastructure.common.Response;
import com.datamate.common.interfaces.PagedResponse; import com.datamate.common.interfaces.PagedResponse;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.util.List; import java.util.List;
@@ -19,41 +17,41 @@ public class CleaningTaskController {
private final CleaningTaskService cleaningTaskService; private final CleaningTaskService cleaningTaskService;
@GetMapping @GetMapping
public ResponseEntity<Response<PagedResponse<CleaningTaskDto>>> cleaningTasksGet( public PagedResponse<CleaningTaskDto> cleaningTasksGet(
@RequestParam("page") Integer page, @RequestParam("page") Integer page,
@RequestParam("size") Integer size, @RequestParam(value = "status", required = false) String status, @RequestParam("size") Integer size, @RequestParam(value = "status", required = false) String status,
@RequestParam(value = "keywords", required = false) String keywords) { @RequestParam(value = "keywords", required = false) String keywords) {
List<CleaningTaskDto> tasks = cleaningTaskService.getTasks(status, keywords, page, size); List<CleaningTaskDto> tasks = cleaningTaskService.getTasks(status, keywords, page, size);
int count = cleaningTaskService.countTasks(status, keywords); int count = cleaningTaskService.countTasks(status, keywords);
int totalPages = (count + size + 1) / size; 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 @PostMapping
public ResponseEntity<Response<CleaningTaskDto>> cleaningTasksPost(@RequestBody CreateCleaningTaskRequest request) { public CleaningTaskDto cleaningTasksPost(@RequestBody CreateCleaningTaskRequest request) {
return ResponseEntity.ok(Response.ok(cleaningTaskService.createTask(request))); return cleaningTaskService.createTask(request);
} }
@PostMapping("/{taskId}/stop") @PostMapping("/{taskId}/stop")
public ResponseEntity<Response<Object>> cleaningTasksStop(@PathVariable("taskId") String taskId) { public String cleaningTasksStop(@PathVariable("taskId") String taskId) {
cleaningTaskService.stopTask(taskId); cleaningTaskService.stopTask(taskId);
return ResponseEntity.ok(Response.ok(null)); return taskId;
} }
@PostMapping("/{taskId}/execute") @PostMapping("/{taskId}/execute")
public ResponseEntity<Response<Object>> cleaningTasksStart(@PathVariable("taskId") String taskId) { public String cleaningTasksStart(@PathVariable("taskId") String taskId) {
cleaningTaskService.executeTask(taskId); cleaningTaskService.executeTask(taskId);
return ResponseEntity.ok(Response.ok(null)); return taskId;
} }
@GetMapping("/{taskId}") @GetMapping("/{taskId}")
public ResponseEntity<Response<CleaningTaskDto>> cleaningTasksTaskIdGet(@PathVariable("taskId") String taskId) { public CleaningTaskDto cleaningTasksTaskIdGet(@PathVariable("taskId") String taskId) {
return ResponseEntity.ok(Response.ok(cleaningTaskService.getTask(taskId))); return cleaningTaskService.getTask(taskId);
} }
@DeleteMapping("/{taskId}") @DeleteMapping("/{taskId}")
public ResponseEntity<Response<Object>> cleaningTasksTaskIdDelete(@PathVariable("taskId") String taskId) { public String cleaningTasksTaskIdDelete(@PathVariable("taskId") String taskId) {
cleaningTaskService.deleteTask(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.CleaningTemplateDto;
import com.datamate.cleaning.interfaces.dto.CreateCleaningTemplateRequest; import com.datamate.cleaning.interfaces.dto.CreateCleaningTemplateRequest;
import com.datamate.cleaning.interfaces.dto.UpdateCleaningTemplateRequest; import com.datamate.cleaning.interfaces.dto.UpdateCleaningTemplateRequest;
import com.datamate.common.infrastructure.common.Response;
import com.datamate.common.interfaces.PagedResponse; import com.datamate.common.interfaces.PagedResponse;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
@@ -29,14 +27,14 @@ public class CleaningTemplateController {
private final CleaningTemplateService cleaningTemplateService; private final CleaningTemplateService cleaningTemplateService;
@GetMapping @GetMapping
public ResponseEntity<Response<PagedResponse<CleaningTemplateDto>>> cleaningTemplatesGet( public PagedResponse<CleaningTemplateDto> cleaningTemplatesGet(
@RequestParam(value = "page", required = false) Integer page, @RequestParam(value = "page", required = false) Integer page,
@RequestParam(value = "size", required = false) Integer size, @RequestParam(value = "size", required = false) Integer size,
@RequestParam(value = "keywords", required = false) String keyword) { @RequestParam(value = "keywords", required = false) String keyword) {
List<CleaningTemplateDto> templates = cleaningTemplateService.getTemplates(keyword); List<CleaningTemplateDto> templates = cleaningTemplateService.getTemplates(keyword);
if (page == null || size == null) { if (page == null || size == null) {
return ResponseEntity.ok(Response.ok(PagedResponse.of(templates.stream() return PagedResponse.of(templates.stream()
.sorted(Comparator.comparing(CleaningTemplateDto::getCreatedAt).reversed()).toList()))); .sorted(Comparator.comparing(CleaningTemplateDto::getCreatedAt).reversed()).toList());
} }
int count = templates.size(); int count = templates.size();
int totalPages = (count + size + 1) / size; int totalPages = (count + size + 1) / size;
@@ -44,31 +42,31 @@ public class CleaningTemplateController {
.sorted(Comparator.comparing(CleaningTemplateDto::getCreatedAt).reversed()) .sorted(Comparator.comparing(CleaningTemplateDto::getCreatedAt).reversed())
.skip((long) page * size) .skip((long) page * size)
.limit(size).toList(); .limit(size).toList();
return ResponseEntity.ok(Response.ok(PagedResponse.of(limitTemplates, page, count, totalPages))); return PagedResponse.of(limitTemplates, page, count, totalPages);
} }
@PostMapping @PostMapping
public ResponseEntity<Response<CleaningTemplateDto>> cleaningTemplatesPost( public CleaningTemplateDto cleaningTemplatesPost(
@RequestBody CreateCleaningTemplateRequest request) { @RequestBody CreateCleaningTemplateRequest request) {
return ResponseEntity.ok(Response.ok(cleaningTemplateService.createTemplate(request))); return cleaningTemplateService.createTemplate(request);
} }
@GetMapping("/{templateId}") @GetMapping("/{templateId}")
public ResponseEntity<Response<CleaningTemplateDto>> cleaningTemplatesTemplateIdGet( public CleaningTemplateDto cleaningTemplatesTemplateIdGet(
@PathVariable("templateId") String templateId) { @PathVariable("templateId") String templateId) {
return ResponseEntity.ok(Response.ok(cleaningTemplateService.getTemplate(templateId))); return cleaningTemplateService.getTemplate(templateId);
} }
@PutMapping("/{templateId}") @PutMapping("/{templateId}")
public ResponseEntity<Response<CleaningTemplateDto>> cleaningTemplatesTemplateIdPut( public CleaningTemplateDto cleaningTemplatesTemplateIdPut(
@PathVariable("templateId") String templateId, @RequestBody UpdateCleaningTemplateRequest request) { @PathVariable("templateId") String templateId, @RequestBody UpdateCleaningTemplateRequest request) {
return ResponseEntity.ok(Response.ok(cleaningTemplateService.updateTemplate(templateId, request))); return cleaningTemplateService.updateTemplate(templateId, request);
} }
@DeleteMapping("/{templateId}") @DeleteMapping("/{templateId}")
public ResponseEntity<Response<Object>> cleaningTemplatesTemplateIdDelete( public String cleaningTemplatesTemplateIdDelete(
@PathVariable("templateId") String templateId) { @PathVariable("templateId") String templateId) {
cleaningTemplateService.deleteTemplate(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.CategoryDto;
import com.datamate.operator.interfaces.dto.CategoryRelationDto; import com.datamate.operator.interfaces.dto.CategoryRelationDto;
import com.datamate.operator.interfaces.dto.CategoryTreeResponse; import com.datamate.operator.interfaces.dto.CategoryTreeResponse;
import com.datamate.operator.interfaces.dto.SubCategory;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Service @Service
@@ -26,37 +29,40 @@ public class CategoryService {
List<CategoryDto> allCategories = categoryRepo.findAllCategories(); List<CategoryDto> allCategories = categoryRepo.findAllCategories();
List<CategoryRelationDto> allRelations = categoryRelationRepo.findAllRelation(); List<CategoryRelationDto> allRelations = categoryRelationRepo.findAllRelation();
Map<Integer, Integer> relationMap = allRelations.stream() Map<String, Integer> relationMap = allRelations.stream()
.collect(Collectors.groupingBy( .collect(Collectors.groupingBy(
CategoryRelationDto::getCategoryId, CategoryRelationDto::getCategoryId,
Collectors.collectingAndThen(Collectors.counting(), Math::toIntExact))); Collectors.collectingAndThen(Collectors.counting(), Math::toIntExact)));
Map<Integer, String> nameMap = allCategories.stream() Map<String, CategoryDto> nameMap = allCategories.stream()
.collect(Collectors.toMap(CategoryDto::getId, CategoryDto::getName)); .collect(Collectors.toMap(CategoryDto::getId, Function.identity()));
Map<Integer, List<CategoryDto>> groupedByParentId = allCategories.stream() Map<String, List<CategoryDto>> groupedByParentId = allCategories.stream()
.filter(relation -> relation.getParentId() > 0) .filter(relation -> !StringUtils.equals(relation.getParentId(), "0"))
.collect(Collectors.groupingBy(CategoryDto::getParentId)); .collect(Collectors.groupingBy(CategoryDto::getParentId));
return groupedByParentId.entrySet().stream() return groupedByParentId.entrySet().stream()
.sorted(Map.Entry.comparingByKey()) .sorted(categoryComparator(nameMap))
.map(entry -> { .map(entry -> {
Integer parentId = entry.getKey(); String parentId = entry.getKey();
List<CategoryDto> group = entry.getValue(); List<CategoryDto> group = entry.getValue();
CategoryTreeResponse response = new CategoryTreeResponse(); CategoryTreeResponse response = new CategoryTreeResponse();
response.setId(parentId); response.setId(parentId);
response.setName(nameMap.get(parentId)); response.setName(nameMap.get(parentId).getName());
AtomicInteger totalCount = new AtomicInteger(); AtomicInteger totalCount = new AtomicInteger();
response.setCategories(group.stream().map(category -> { response.setCategories(group.stream().peek(category -> {
SubCategory subCategory = new SubCategory(); category.setCount(relationMap.getOrDefault(category.getId(), 0));
subCategory.setId(category.getId());
subCategory.setName(category.getName());
subCategory.setCount(relationMap.getOrDefault(category.getId(), 0));
totalCount.getAndAdd(relationMap.getOrDefault(category.getId(), 0)); totalCount.getAndAdd(relationMap.getOrDefault(category.getId(), 0));
subCategory.setParentId(parentId); }).sorted(Comparator.comparing(CategoryDto::getCreatedAt)).toList());
return subCategory;
}).toList());
response.setCount(totalCount.get()); response.setCount(totalCount.get());
return response; return response;
}).toList(); }).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; 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.infrastructure.converter.OperatorConverter;
import com.datamate.operator.domain.model.OperatorView; import com.datamate.operator.domain.model.OperatorView;
import com.datamate.operator.domain.repository.CategoryRelationRepository; import com.datamate.operator.domain.repository.CategoryRelationRepository;
import com.datamate.operator.domain.repository.OperatorRepository; import com.datamate.operator.domain.repository.OperatorRepository;
import com.datamate.operator.domain.repository.OperatorViewRepository; 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.OperatorDto;
import com.datamate.operator.interfaces.dto.UploadOperatorRequest;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service; 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; import java.util.List;
@Service @Service
@@ -21,14 +28,21 @@ public class OperatorService {
private final CategoryRelationRepository relationRepo; 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) { String operatorName, Boolean isStar) {
List<OperatorView> filteredOperators = operatorViewRepo.findOperatorsByCriteria(page, size, operatorName, List<OperatorView> filteredOperators = operatorViewRepo.findOperatorsByCriteria(page, size, operatorName,
categories, isStar); categories, isStar);
return filteredOperators.stream().map(OperatorConverter.INSTANCE::fromEntityToDto).toList(); 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); return operatorViewRepo.countOperatorsByCriteria(operatorName, categories, isStar);
} }
@@ -37,20 +51,60 @@ public class OperatorService {
return OperatorConverter.INSTANCE.fromEntityToDto(operator); return OperatorConverter.INSTANCE.fromEntityToDto(operator);
} }
@Transactional
public OperatorDto createOperator(OperatorDto req) { public OperatorDto createOperator(OperatorDto req) {
operatorRepo.insertOperator(req); operatorRepo.insertOperator(req);
relationRepo.batchInsert(req.getId(), req.getCategories()); relationRepo.batchInsert(req.getId(), req.getCategories());
parserHolder.extractTo(getFileType(req.getFileName()), getUploadPath(req.getFileName()),
getExtractPath(getFileNameWithoutExtension(req.getFileName())));
return getOperatorById(req.getId()); return getOperatorById(req.getId());
} }
@Transactional
public OperatorDto updateOperator(String id, OperatorDto req) { public OperatorDto updateOperator(String id, OperatorDto req) {
operatorRepo.updateOperator(req); operatorRepo.updateOperator(req);
relationRepo.batchInsert(id, req.getCategories()); relationRepo.batchInsert(id, req.getCategories());
parserHolder.extractTo(getFileType(req.getFileName()), getUploadPath(req.getFileName()),
getExtractPath(getFileNameWithoutExtension(req.getFileName())));
return getOperatorById(id); return getOperatorById(id);
} }
public OperatorDto uploadOperator(MultipartFile file, String description) { @Transactional
// TODO: 文件上传与解析 public void deleteOperator(String id) {
return new OperatorDto(); 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.Getter;
import lombok.Setter; import lombok.Setter;
import java.time.LocalDateTime;
@Setter @Setter
@Getter @Getter
@TableName(value = "t_operator_category", autoResultMap = true) @TableName(value = "t_operator_category", autoResultMap = true)
public class Category { public class Category {
private Integer id; private String id;
private String name; private String name;
private String value;
private String type; private String type;
private Integer parentId; private String parentId;
private LocalDateTime createdAt;
} }

View File

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

View File

@@ -3,7 +3,6 @@ package com.datamate.operator.domain.repository;
import com.baomidou.mybatisplus.extension.repository.IRepository; import com.baomidou.mybatisplus.extension.repository.IRepository;
import com.datamate.operator.domain.model.CategoryRelation; import com.datamate.operator.domain.model.CategoryRelation;
import com.datamate.operator.interfaces.dto.CategoryRelationDto; import com.datamate.operator.interfaces.dto.CategoryRelationDto;
import org.apache.ibatis.annotations.Param;
import java.util.List; import java.util.List;
@@ -11,5 +10,7 @@ public interface CategoryRelationRepository extends IRepository<CategoryRelation
List<CategoryRelationDto> findAllRelation(); 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; import java.util.List;
public interface OperatorRepository extends IRepository<Operator> { public interface OperatorRepository extends IRepository<Operator> {
List<Operator> findAllOperators(); List<OperatorDto> findAllOperators();
void updateOperator(OperatorDto operator); void updateOperator(OperatorDto operator);
void insertOperator(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> { public interface OperatorViewRepository extends IRepository<OperatorView> {
List<OperatorView> findOperatorsByCriteria(Integer page, Integer size, String operatorName, 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); OperatorView findOperatorById(String id);
} }

View File

@@ -1,8 +1,10 @@
package com.datamate.operator.infrastructure.converter; 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.Operator;
import com.datamate.operator.domain.model.OperatorView; import com.datamate.operator.domain.model.OperatorView;
import com.datamate.operator.interfaces.dto.OperatorDto; import com.datamate.operator.interfaces.dto.OperatorDto;
import com.datamate.operator.interfaces.dto.UploadOperatorRequest;
import org.mapstruct.Mapper; import org.mapstruct.Mapper;
import org.mapstruct.Mapping; import org.mapstruct.Mapping;
import org.mapstruct.Named; import org.mapstruct.Named;
@@ -19,13 +21,17 @@ public interface OperatorConverter {
@Mapping(target = "categories", source = "categories", qualifiedByName = "stringToList") @Mapping(target = "categories", source = "categories", qualifiedByName = "stringToList")
OperatorDto fromEntityToDto(OperatorView operator); OperatorDto fromEntityToDto(OperatorView operator);
List<OperatorDto> fromEntityToDto(List<Operator> operator);
@Named("stringToList") @Named("stringToList")
static List<Integer> stringToList(String input) { static List<String> stringToList(String input) {
if (input == null || input.isEmpty()) { if (input == null || input.isEmpty()) {
return Collections.emptyList(); 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); 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; package com.datamate.operator.infrastructure.persistence.Impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.repository.CrudRepository; import com.baomidou.mybatisplus.extension.repository.CrudRepository;
import com.datamate.operator.domain.model.CategoryRelation; import com.datamate.operator.domain.model.CategoryRelation;
import com.datamate.operator.domain.repository.CategoryRelationRepository; import com.datamate.operator.domain.repository.CategoryRelationRepository;
@@ -23,10 +24,17 @@ public class CategoryRelationRepositoryImpl extends CrudRepository<CategoryRelat
} }
@Override @Override
public void batchInsert(String operatorId, List<Integer> categories) { public void batchInsert(String operatorId, List<String> categories) {
List<CategoryRelation> categoryRelations = categories.stream() List<CategoryRelation> categoryRelations = categories.stream()
.map(category -> new CategoryRelation(category, operatorId)) .map(category -> new CategoryRelation(category, operatorId))
.toList(); .toList();
mapper.insert(categoryRelations); 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; private final OperatorMapper mapper;
@Override @Override
public List<Operator> findAllOperators() { public List<OperatorDto> findAllOperators() {
return mapper.selectList(null); return OperatorConverter.INSTANCE.fromEntityToDto(mapper.selectList(null));
} }
@Override @Override
@@ -30,4 +30,9 @@ public class OperatorRepositoryImpl extends CrudRepository<OperatorMapper, Opera
public void insertOperator(OperatorDto operator) { public void insertOperator(OperatorDto operator) {
mapper.insert(OperatorConverter.INSTANCE.fromDtoToEntity(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 @Override
public List<OperatorView> findOperatorsByCriteria(Integer page, Integer size, String operatorName, 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<OperatorView> queryWrapper = Wrappers.query();
queryWrapper.in(CollectionUtils.isNotEmpty(categories), "category_id", categories) queryWrapper.in(CollectionUtils.isNotEmpty(categories), "category_id", categories)
.like(StringUtils.isNotBlank(operatorName), "operator_name", operatorName) .like(StringUtils.isNotBlank(operatorName), "operator_name", operatorName)
@@ -37,7 +37,7 @@ public class OperatorViewRepositoryImpl extends CrudRepository<OperatorViewMappe
} }
@Override @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<OperatorView> queryWrapper = Wrappers.query();
queryWrapper.in(CollectionUtils.isNotEmpty(categories),"category_id", categories) queryWrapper.in(CollectionUtils.isNotEmpty(categories),"category_id", categories)
.like(StringUtils.isNotBlank(operatorName), "operator_name", operatorName) .like(StringUtils.isNotBlank(operatorName), "operator_name", operatorName)

View File

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

View File

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

View File

@@ -12,11 +12,11 @@ import java.util.List;
@Setter @Setter
@NoArgsConstructor @NoArgsConstructor
public class CategoryTreeResponse { public class CategoryTreeResponse {
private Integer id; private String id;
private String name; private String name;
private Integer count; 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 @Getter
@Setter @Setter
public class OperatorDto { 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) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
private LocalDateTime createdAt; private LocalDateTime createdAt;
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
private LocalDateTime updatedAt; private LocalDateTime updatedAt;
} }

View File

@@ -19,7 +19,7 @@ public class OperatorsListPostRequest {
private Integer size; private Integer size;
private List<Integer> categories = new ArrayList<>(); private List<String> categories = new ArrayList<>();
private String operatorName; 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; package com.datamate.operator.interfaces.rest;
import com.datamate.common.infrastructure.common.Response;
import com.datamate.common.interfaces.PagedResponse; import com.datamate.common.interfaces.PagedResponse;
import com.datamate.operator.application.CategoryService; import com.datamate.operator.application.CategoryService;
import com.datamate.operator.interfaces.dto.CategoryTreeResponse; import com.datamate.operator.interfaces.dto.CategoryTreeResponse;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
@@ -20,8 +18,8 @@ public class CategoryController {
private final CategoryService categoryService; private final CategoryService categoryService;
@GetMapping("/tree") @GetMapping("/tree")
public ResponseEntity<Response<PagedResponse<CategoryTreeResponse>>> categoryTreeGet() { public PagedResponse<CategoryTreeResponse> categoryTreeGet() {
List<CategoryTreeResponse> allCategories = categoryService.getAllCategories(); 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; package com.datamate.operator.interfaces.rest;
import com.datamate.common.infrastructure.common.Response;
import com.datamate.common.interfaces.PagedResponse; import com.datamate.common.interfaces.PagedResponse;
import com.datamate.operator.application.OperatorService; import com.datamate.operator.application.OperatorService;
import com.datamate.operator.interfaces.dto.OperatorDto; import com.datamate.operator.interfaces.dto.OperatorDto;
import com.datamate.operator.interfaces.dto.OperatorsListPostRequest; import com.datamate.operator.interfaces.dto.OperatorsListPostRequest;
import com.datamate.operator.interfaces.dto.UploadOperatorRequest;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity; import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.List; import java.util.List;
@@ -19,34 +18,48 @@ public class OperatorController {
private final OperatorService operatorService; private final OperatorService operatorService;
@PostMapping("/list") @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(), List<OperatorDto> responses = operatorService.getOperators(request.getPage(), request.getSize(),
request.getCategories(), request.getOperatorName(), request.getIsStar()); request.getCategories(), request.getOperatorName(), request.getIsStar());
int count = operatorService.getOperatorsCount(request.getCategories(), request.getOperatorName(), int count = operatorService.getOperatorsCount(request.getCategories(), request.getOperatorName(),
request.getIsStar()); request.getIsStar());
int totalPages = (count + request.getSize() + 1) / request.getSize(); 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}") @GetMapping("/{id}")
public ResponseEntity<Response<OperatorDto>> operatorsIdGet(@PathVariable("id") String id) { public OperatorDto operatorsIdGet(@PathVariable("id") String id) {
return ResponseEntity.ok(Response.ok(operatorService.getOperatorById(id))); return operatorService.getOperatorById(id);
} }
@PutMapping("/{id}") @PutMapping("/{id}")
public ResponseEntity<Response<OperatorDto>> operatorsIdPut(@PathVariable("id") String id, public OperatorDto operatorsIdPut(@PathVariable("id") String id,
@RequestBody OperatorDto updateOperatorRequest) { @RequestBody OperatorDto updateOperatorRequest) {
return ResponseEntity.ok(Response.ok(operatorService.updateOperator(id, updateOperatorRequest))); return operatorService.updateOperator(id, updateOperatorRequest);
} }
@PostMapping("/create") @PostMapping("/create")
public ResponseEntity<Response<OperatorDto>> operatorsCreatePost(@RequestBody OperatorDto createOperatorRequest) { public OperatorDto operatorsCreatePost(@RequestBody OperatorDto createOperatorRequest) {
return ResponseEntity.ok(Response.ok(operatorService.createOperator(createOperatorRequest))); return operatorService.createOperator(createOperatorRequest);
} }
@PostMapping("/upload") @PostMapping("/upload")
public ResponseEntity<Response<OperatorDto>> operatorsUploadPost(@RequestPart(value = "file") MultipartFile file, public OperatorDto operatorsUploadPost(@RequestBody UploadOperatorRequest request) {
@RequestParam(value = "description") String description) { return operatorService.uploadOperator(request.getFileName());
return ResponseEntity.ok(Response.ok(operatorService.uploadOperator(file, description))); }
@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 - dataset_volume:/dataset
- flow_volume:/flow - flow_volume:/flow
- log_volume:/var/log/datamate - log_volume:/var/log/datamate
- operator-upload-volume:/operators/upload
- operator-runtime-volume:/operators/extract
networks: [ datamate ] networks: [ datamate ]
depends_on: depends_on:
- datamate-database - datamate-database
@@ -71,6 +73,7 @@ services:
- log_volume:/var/log/datamate - log_volume:/var/log/datamate
- dataset_volume:/dataset - dataset_volume:/dataset
- flow_volume:/flow - flow_volume:/flow
- operator-runtime-volume:/opt/runtime/datamate/ops/user
networks: [ datamate ] networks: [ datamate ]
# 4) mineru # 4) mineru
@@ -109,6 +112,10 @@ volumes:
name: datamate-frontend-log-volume name: datamate-frontend-log-volume
database_log_volume: database_log_volume:
name: datamate-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: networks:
datamate: 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: {} securityContext: {}
# Optional: The following volumes/volumeMounts configurations are optional but recommended because # Optional: The following volumes/volumeMounts configurations are optional but recommended because
# Ray writes logs to /tmp/ray/session_latests/logs instead of stdout/stderr. # Ray writes logs to /tmp/ray/session_latests/logs instead of stdout/stderr.
volumes: volumes: []
- name: log-volume volumeMounts: []
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
# sidecarContainers specifies additional containers to attach to the Ray pod. # sidecarContainers specifies additional containers to attach to the Ray pod.
# Follows standard K8s container spec. # Follows standard K8s container spec.
sidecarContainers: 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
# See docs/guidance/pod-command.md for more details about how to specify # See docs/guidance/pod-command.md for more details about how to specify
# container command for head Pod. # container command for head Pod.
command: [] command: []
@@ -260,27 +208,8 @@ worker:
securityContext: {} securityContext: {}
# Optional: The following volumes/volumeMounts configurations are optional but recommended because # Optional: The following volumes/volumeMounts configurations are optional but recommended because
# Ray writes logs to /tmp/ray/session_latests/logs instead of stdout/stderr. # Ray writes logs to /tmp/ray/session_latests/logs instead of stdout/stderr.
volumes: volumes: []
- name: log-volume volumeMounts: []
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
# sidecarContainers specifies additional containers to attach to the Ray pod. # sidecarContainers specifies additional containers to attach to the Ray pod.
# Follows standard K8s container spec. # Follows standard K8s container spec.
sidecarContainers: [] sidecarContainers: []

View File

@@ -40,11 +40,17 @@ dataVolume: &dataVolume
path: /opt/datamate/data/mysql path: /opt/datamate/data/mysql
type: DirectoryOrCreate type: DirectoryOrCreate
operatorVolume: &operatorVolume
name: operator-volume
hostPath:
path: /opt/datamate/data/operator
backend: backend:
volumes: volumes:
- *datasetVolume - *datasetVolume
- *flowVolume - *flowVolume
- *logVolume - *logVolume
- *operatorVolume
volumeMounts: volumeMounts:
- name: dataset-volume - name: dataset-volume
mountPath: /dataset mountPath: /dataset
@@ -52,14 +58,22 @@ backend:
mountPath: /flow mountPath: /flow
- name: log-volume - name: log-volume
mountPath: /var/log/datamate mountPath: /var/log/datamate
- name: operator-volume
mountPath: /operators
frontend: frontend:
volumes: volumes:
- *logVolume - *logVolume
- name: datamate-nginx-conf
configMap:
name: datamate-nginx-conf
volumeMounts: volumeMounts:
- name: log-volume - name: log-volume
mountPath: /var/log/datamate/frontend mountPath: /var/log/datamate/frontend
subPath: frontend subPath: frontend
- mountPath: /etc/nginx/conf.d/backend.conf
name: datamate-nginx-conf
subPath: backend.conf
database: database:
volumes: volumes:
@@ -81,3 +95,76 @@ database:
mountPath: /docker-entrypoint-initdb.d mountPath: /docker-entrypoint-initdb.d
- name: mysql-utf8-config - name: mysql-utf8-config
mountPath: /etc/mysql/conf.d 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 CREATE TABLE IF NOT EXISTS t_clean_template
( (
id varchar(64) primary key not null unique, id varchar(64) primary key not null unique,
name varchar(64), name varchar(64) unique,
description varchar(256), description varchar(256),
created_at timestamp default current_timestamp, created_at timestamp default current_timestamp,
updated_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 CREATE TABLE IF NOT EXISTS t_clean_task
( (
id varchar(64) primary key, id varchar(64) primary key,
name varchar(64), name varchar(64) unique,
description varchar(256), description varchar(256),
status varchar(256), status varchar(256),
src_dataset_id varchar(64), src_dataset_id varchar(64),

View File

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

View File

@@ -14,10 +14,9 @@ RUN cd DataX && \
FROM maven:3-amazoncorretto-21-debian AS builder FROM maven:3-amazoncorretto-21-debian AS builder
COPY backend/ /opt/backend COPY backend/ /opt/backend
COPY scripts/images/backend/settings.xml /opt/backend
RUN cd /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 FROM openjdk:21-jdk-slim
@@ -25,7 +24,7 @@ FROM openjdk:21-jdk-slim
RUN apt-get update && \ RUN apt-get update && \
apt-get install -y vim wget curl nfs-common rsync python3 python3-pip python-is-python3 dos2unix && \ apt-get install -y vim wget curl nfs-common rsync python3 python3-pip python-is-python3 dos2unix && \
apt-get clean && \ 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=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 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/python-executor /opt/runtime
COPY runtime/ops /opt/runtime/datamate/ops 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/ ENV PYTHONPATH=/opt/runtime/datamate/
@@ -12,12 +14,13 @@ RUN apt update \
WORKDIR /opt/runtime WORKDIR /opt/runtime
ENV HF_HUB_DISABLE_XET=1 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 \
RUN pip install -e . \
&& pip install -r /opt/runtime/datamate/ops/requirements.txt \
&& pip cache purge && 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 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 "$@"