Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package site.icebang.domain.workflow.controller;

import java.util.Map;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;

import site.icebang.domain.workflow.dto.JobDto;
import site.icebang.domain.workflow.service.WorkflowService;

@RestController
@RequestMapping("/v0/jobs")
@RequiredArgsConstructor
public class JobController {

private final WorkflowService workflowService;

@PostMapping
public ResponseEntity<Map<String, Object>> createJob(@Valid @RequestBody JobDto dto) {
JobDto created = workflowService.createJob(dto);
return ResponseEntity.status(HttpStatus.CREATED).body(Map.of("success", true, "data", created));
}

@GetMapping("/{id}")
public ResponseEntity<Map<String, Object>> getJob(@PathVariable Long id) {
JobDto job = workflowService.findJobById(id);
if (job == null) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(Map.of("success", false));
}
return ResponseEntity.ok(Map.of("success", true, "data", job));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package site.icebang.domain.workflow.controller;

import java.util.Map;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;

import site.icebang.domain.workflow.dto.TaskDto;
import site.icebang.domain.workflow.service.WorkflowService;

@RestController
@RequestMapping("/v0/tasks")
@RequiredArgsConstructor
public class TaskController {

private final WorkflowService workflowService;

@PostMapping
public ResponseEntity<Map<String, Object>> createTask(@Valid @RequestBody TaskDto dto) {
TaskDto created = workflowService.createTask(dto);
return ResponseEntity.status(HttpStatus.CREATED).body(Map.of("success", true, "data", created));
}

@GetMapping("/{id}")
public ResponseEntity<Map<String, Object>> getTask(@PathVariable Long id) {
TaskDto task = workflowService.findTaskById(id);
if (task == null) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(Map.of("success", false));
}
return ResponseEntity.ok(Map.of("success", true, "data", task));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@

import java.time.Instant;

import jakarta.validation.constraints.NotBlank;
import lombok.Data;

@Data
public class JobDto {
private Long id;

@NotBlank(message = "Job 이름은 필수입니다")
private String name;

private String description;
private Boolean isEnabled;
private Instant createdAt;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@

import com.fasterxml.jackson.databind.JsonNode;

import jakarta.validation.constraints.NotBlank;
import lombok.Data;

@Data
public class TaskDto {
private Long id;

@NotBlank(message = "Task 이름은 필수입니다")
private String name;

private String type;
private Integer executionOrder;
private JsonNode settings;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,8 @@ public interface JobMapper {
List<JobDto> findJobsByWorkflowId(Long workflowId);

List<TaskDto> findTasksByJobId(Long jobId);

JobDto findJobById(Long id);

void insertJob(JobDto job);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@

import org.apache.ibatis.annotations.Mapper;

import site.icebang.domain.workflow.dto.TaskDto;
import site.icebang.domain.workflow.model.Task;

@Mapper
public interface TaskMapper {
Optional<Task> findById(Long id);

void insertTask(TaskDto task);

TaskDto findTaskById(Long id);
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public Job(JobDto dto) {
this.id = dto.getId();
this.name = dto.getName();
this.description = dto.getDescription();
this.isEnabled = dto.getIsEnabled();
this.isEnabled = Boolean.TRUE.equals(dto.getIsEnabled());
this.createdAt = dto.getCreatedAt();
this.updatedAt = dto.getUpdatedAt();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,7 @@ public Task(TaskDto taskDto) {
this.type = taskDto.getType();
this.settings = taskDto.getSettings();
this.parameters = taskDto.getParameters();
this.createdAt = taskDto.getCreatedAt();
this.updatedAt = taskDto.getUpdatedAt();
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package site.icebang.domain.workflow.service;

import java.math.BigInteger;
import java.time.Instant;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
Expand All @@ -21,11 +22,9 @@
import site.icebang.domain.schedule.mapper.ScheduleMapper;
import site.icebang.domain.schedule.model.Schedule;
import site.icebang.domain.schedule.service.QuartzScheduleService;
import site.icebang.domain.workflow.dto.ScheduleCreateDto;
import site.icebang.domain.workflow.dto.ScheduleDto;
import site.icebang.domain.workflow.dto.WorkflowCardDto;
import site.icebang.domain.workflow.dto.WorkflowCreateDto;
import site.icebang.domain.workflow.dto.WorkflowDetailCardDto;
import site.icebang.domain.workflow.dto.*;
import site.icebang.domain.workflow.mapper.JobMapper;
import site.icebang.domain.workflow.mapper.TaskMapper;
import site.icebang.domain.workflow.mapper.WorkflowMapper;

/**
Expand All @@ -51,6 +50,8 @@ public class WorkflowService implements PageableService<WorkflowCardDto> {
private final WorkflowMapper workflowMapper;
private final ScheduleMapper scheduleMapper;
private final QuartzScheduleService quartzScheduleService;
private final JobMapper jobMapper;
private final TaskMapper taskMapper;

/**
* 워크플로우 목록을 페이징 처리하여 조회합니다.
Expand Down Expand Up @@ -166,6 +167,87 @@ public void createWorkflow(WorkflowCreateDto dto, BigInteger createdBy) {
}
}

/**
* Job 생성
*
* @param dto Job 생성 정보
* @return 생성된 Job 정보
* @throws IllegalArgumentException Job 이름이 필수인데 없거나 빈 값일 경우
*/
@Transactional
public JobDto createJob(JobDto dto) {
// 1. 유효성 검증
if (dto.getName() == null || dto.getName().isBlank()) {
throw new IllegalArgumentException("job name is required");
}

// 2. 시간 정보 설정
Instant now = Instant.now();
dto.setCreatedAt(now);
dto.setUpdatedAt(now);

// 3. 생성자 정보 설정 (현재 사용자 정보가 없으므로 기본값 또는 추후 개선)
// dto.setCreatedBy(getCurrentUserId());
// dto.setUpdatedBy(getCurrentUserId());

// 4. DB 저장
jobMapper.insertJob(dto);

// 5. 저장된 Job 반환
return jobMapper.findJobById(dto.getId());
}

/**
* Job ID로 Job 조회
*
* @param id Job ID
* @return Job 정보, 없으면 null
*/
@Transactional(readOnly = true)
public JobDto findJobById(Long id) {
return jobMapper.findJobById(id);
}

/**
* Task 생성
*
* @param dto Task 생성 정보
* @return 생성된 Task 정보
* @throws IllegalArgumentException Task 이름이 필수인데 없거나 빈 값일 경우
*/
@Transactional
public TaskDto createTask(TaskDto dto) {
// 1. 유효성 검증
if (dto.getName() == null || dto.getName().isBlank()) {
throw new IllegalArgumentException("task name is required");
}
// 2. 시간 정보 설정
Instant now = Instant.now();
dto.setCreatedAt(now);
dto.setUpdatedAt(now);

// 3. 생성자 정보 설정 (현재 사용자 정보가 없으므로 기본값 또는 추후 개선)
// dto.setCreatedBy(getCurrentUserId());
// dto.setUpdatedBy(getCurrentUserId());

// 4. DB 저장
taskMapper.insertTask(dto);

// 5. 저장된 Task 반환
return taskMapper.findTaskById(dto.getId());
}

/**
* Task ID로 Task 조회
*
* @param id Task ID
* @return Task 정보, 없으면 null
*/
@Transactional(readOnly = true)
public TaskDto findTaskById(Long id) {
return taskMapper.findTaskById(id);
}

/** 기본 입력값 검증 */
private void validateBasicInput(WorkflowCreateDto dto, BigInteger createdBy) {
if (dto == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,4 +118,17 @@ public ApiResponse<String> handleDuplicateData(DuplicateDataException ex) {
log.warn(ex.getMessage(), ex);
return ApiResponse.error("Duplicate: " + ex.getMessage(), HttpStatus.CONFLICT);
}

/**
* IllegalArgumentException을 400 Bad Request로 처리합니다. WorkflowService에서 던지는 검증 오류를 처리하기 위해 추가되었습니다.
*
* @param ex 발생한 {@link IllegalArgumentException}
* @return {@link ApiResponse} - 검증 실패 메시지와 {@link HttpStatus#BAD_REQUEST}
*/
@ExceptionHandler(IllegalArgumentException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ApiResponse<String> handleIllegalArgument(IllegalArgumentException ex) {
log.warn("Validation failed: {}", ex.getMessage());
return ApiResponse.error("입력값 검증 실패: " + ex.getMessage(), HttpStatus.BAD_REQUEST);
}
}
27 changes: 27 additions & 0 deletions apps/user-service/src/main/resources/mybatis/mapper/JobMapper.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,33 @@
<result property="executionOrder" column="execution_order"/>
</resultMap>

<insert id="insertJob" parameterType="site.icebang.domain.workflow.dto.JobDto"
useGeneratedKeys="true" keyProperty="id">
INSERT INTO job (
name,
description,
is_enabled,
created_at,
created_by,
updated_at,
updated_by
) VALUES (
#{name},
#{description},
#{isEnabled},
#{createdAt},
#{createdBy},
#{updatedAt},
#{updatedBy}
)
</insert>

<select id="findJobById" resultMap="JobDtoResultMap">
SELECT j.*
FROM job j
WHERE j.id = #{id}
</select>

<select id="findJobsByWorkflowId" resultMap="JobDtoResultMap">
SELECT
j.*,
Expand Down
34 changes: 34 additions & 0 deletions apps/user-service/src/main/resources/mybatis/mapper/TaskMapper.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,40 @@
<result property="updatedAt" column="updated_at" javaType="java.time.Instant" jdbcType="TIMESTAMP" typeHandler="site.icebang.global.config.mybatis.typehandler.InstantTypeHandler"/>
</resultMap>

<resultMap id="TaskDtoResultMap" type="site.icebang.domain.workflow.dto.TaskDto">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="type" column="type"/>
<result property="parameters" column="parameters" javaType="com.fasterxml.jackson.databind.JsonNode" jdbcType="VARCHAR" typeHandler="site.icebang.global.config.mybatis.typehandler.JsonNodeTypeHandler"/>
<result property="createdAt" column="created_at" javaType="java.time.Instant" jdbcType="TIMESTAMP" typeHandler="site.icebang.global.config.mybatis.typehandler.InstantTypeHandler"/>
<result property="updatedAt" column="updated_at" javaType="java.time.Instant" jdbcType="TIMESTAMP" typeHandler="site.icebang.global.config.mybatis.typehandler.InstantTypeHandler"/>
<result property="executionOrder" column="execution_order"/>
<result property="settings" column="settings" javaType="com.fasterxml.jackson.databind.JsonNode" jdbcType="VARCHAR" typeHandler="site.icebang.global.config.mybatis.typehandler.JsonNodeTypeHandler"/>
</resultMap>

<insert id="insertTask" parameterType="site.icebang.domain.workflow.dto.TaskDto"
useGeneratedKeys="true" keyProperty="id">
INSERT INTO task (
name,
type,
parameters,
created_at,
updated_at
) VALUES (
#{name},
#{type},
#{parameters, javaType=com.fasterxml.jackson.databind.JsonNode, jdbcType=VARCHAR, typeHandler=site.icebang.global.config.mybatis.typehandler.JsonNodeTypeHandler},
#{createdAt},
#{updatedAt}
)
</insert>

<select id="findTaskById" resultMap="TaskDtoResultMap">
SELECT t.*
FROM task t
WHERE t.id = #{id}
</select>

<select id="findById" resultMap="TaskResultMap">
SELECT * FROM task
WHERE id = #{taskId}
Expand Down
Loading
Loading