Skip to content
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package site.icebang.domain.workflow.controller;

import java.math.BigInteger;

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

Expand All @@ -9,8 +11,8 @@
import site.icebang.common.dto.PageParams;
import site.icebang.common.dto.PageResult;
import site.icebang.domain.workflow.dto.WorkflowCardDto;
import site.icebang.domain.workflow.dto.WorkflowDetailCardDto;
import site.icebang.domain.workflow.service.WorkflowExecutionService;
import site.icebang.domain.workflow.service.WorkflowHistoryService;
import site.icebang.domain.workflow.service.WorkflowService;

@RestController
Expand All @@ -19,7 +21,6 @@
public class WorkflowController {
private final WorkflowService workflowService;
private final WorkflowExecutionService workflowExecutionService;
private final WorkflowHistoryService workflowHistoryService;

@GetMapping("")
public ApiResponse<PageResult<WorkflowCardDto>> getWorkflowList(
Expand All @@ -34,4 +35,10 @@ public ResponseEntity<Void> runWorkflow(@PathVariable Long workflowId) {
workflowExecutionService.executeWorkflow(workflowId);
return ResponseEntity.accepted().build();
}

@GetMapping("/{workflowId}/detail")
public ApiResponse<WorkflowDetailCardDto> getWorkflowDetail(@PathVariable BigInteger workflowId) {
WorkflowDetailCardDto result = workflowService.getWorkflowDetail(workflowId);
return ApiResponse.success(result);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package site.icebang.domain.workflow.dto;

import java.time.LocalDateTime;

import lombok.Data;

@Data
public class ScheduleDto {
private Long id;
private String cronExpression;
private Boolean isActive;
private String lastRunStatus;
private LocalDateTime lastRunAt;
private String scheduleText;
private LocalDateTime createdAt;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package site.icebang.domain.workflow.dto;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;

import lombok.Data;

@Data
public class WorkflowDetailCardDto extends WorkflowCardDto {
private String defaultConfig;
private LocalDateTime updatedAt;
private String updatedBy;
private List<ScheduleDto> schedules;
private List<Map<String, Object>> jobs;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,20 @@
import java.util.*;

import site.icebang.common.dto.PageParams;
import site.icebang.domain.workflow.dto.ScheduleDto;
import site.icebang.domain.workflow.dto.WorkflowCardDto;
import site.icebang.domain.workflow.dto.WorkflowDetailCardDto;

public interface WorkflowMapper {
List<WorkflowCardDto> selectWorkflowList(PageParams pageParams);

int selectWorkflowCount(PageParams pageParams);

WorkflowCardDto selectWorkflowById(BigInteger id);

WorkflowDetailCardDto selectWorkflowDetailById(BigInteger workflowId);

List<ScheduleDto> selectSchedulesByWorkflowId(BigInteger workflowId);

List<Map<String, Object>> selectWorkflowWithJobsAndTasks(BigInteger workflowId);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package site.icebang.domain.workflow.service;

import java.math.BigInteger;
import java.util.List;
import java.util.Map;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

Expand All @@ -8,7 +12,9 @@
import site.icebang.common.dto.PageParams;
import site.icebang.common.dto.PageResult;
import site.icebang.common.service.PageableService;
import site.icebang.domain.workflow.dto.ScheduleDto;
import site.icebang.domain.workflow.dto.WorkflowCardDto;
import site.icebang.domain.workflow.dto.WorkflowDetailCardDto;
import site.icebang.domain.workflow.mapper.WorkflowMapper;

@Service
Expand All @@ -25,4 +31,23 @@ public PageResult<WorkflowCardDto> getPagedResult(PageParams pageParams) {
() -> workflowMapper.selectWorkflowList(pageParams),
() -> workflowMapper.selectWorkflowCount(pageParams));
}

@Transactional(readOnly = true)
public WorkflowDetailCardDto getWorkflowDetail(BigInteger workflowId) {

// 1. 워크플로우 기본 정보 조회 (단일 row, 효율적)
WorkflowDetailCardDto workflow = workflowMapper.selectWorkflowDetailById(workflowId);
if (workflow == null) {
throw new IllegalArgumentException("워크플로우를 찾을 수 없습니다: " + workflowId);
}

// 2. 스케줄 목록 조회 (별도 쿼리로 성능 최적화)
List<ScheduleDto> schedules = workflowMapper.selectSchedulesByWorkflowId(workflowId);
workflow.setSchedules(schedules);

List<Map<String, Object>> jobs = workflowMapper.selectWorkflowWithJobsAndTasks(workflowId);
workflow.setJobs(jobs);

return workflow;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,94 @@
LEFT JOIN user u ON w.created_by = u.id
WHERE w.id = #{id}
</select>

<resultMap id="WorkflowDetailMap" type="site.icebang.domain.workflow.dto.WorkflowDetailCardDto">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="description" column="description"/>
<result property="isEnabled" column="isEnabled"/>
<result property="defaultConfig" column="defaultConfig"/>
<result property="createdBy" column="createdBy"/>
<result property="createdAt" column="createdAt"/>
<result property="updatedBy" column="updatedBy"/>
<result property="updatedAt" column="updatedAt"/>
</resultMap>

<!-- 스케줄 정보 매핑 -->
<resultMap id="ScheduleMap" type="site.icebang.domain.workflow.dto.ScheduleDto">
<id property="id" column="id"/>
<result property="cronExpression" column="cronExpression"/>
<result property="isActive" column="isActive"/>
<result property="lastRunStatus" column="lastRunStatus"/>
<result property="lastRunAt" column="lastRunAt"/>
<result property="scheduleText" column="scheduleText"/>
<result property="createdAt" column="createdAt"/>
</resultMap>

<!-- 워크플로우 기본 정보 조회 -->
<select id="selectWorkflowDetailById" parameterType="BigInteger"
resultMap="WorkflowDetailMap">
SELECT
w.id,
w.name,
w.description,
w.is_enabled AS isEnabled,
w.default_config AS defaultConfig,
creator.name AS createdBy,
w.created_at AS createdAt,
updater.name AS updatedBy,
w.updated_at AS updatedAt
FROM workflow w
LEFT JOIN `user` creator ON w.created_by = creator.id
LEFT JOIN `user` updater ON w.updated_by = updater.id
WHERE w.id = #{workflowId}
</select>

<!-- 워크플로우별 스케줄 목록 조회 -->
<select id="selectSchedulesByWorkflowId" parameterType="BigInteger"
resultMap="ScheduleMap">
SELECT
id,
cron_expression AS cronExpression,
is_active AS isActive,
last_run_status AS lastRunStatus,
last_run_at AS lastRunAt,
schedule_text AS scheduleText,
created_at AS createdAt
FROM schedule
WHERE workflow_id = #{workflowId}
ORDER BY id
</select>

<select id="selectWorkflowWithJobsAndTasks" parameterType="BigInteger"
resultType="java.util.Map">
SELECT
w.id as workflow_id,
wj.execution_order as job_execution_order,
j.id as job_id,
j.name as job_name,
j.description as job_description,
j.is_enabled as job_enabled,
-- Job별 Task 정보를 JSON 배열로 그룹화
JSON_ARRAYAGG(
JSON_OBJECT(
'task_id', t.id,
'task_name', t.name,
'task_type', t.type,
'task_parameters', t.parameters,
'execution_order', jt.execution_order
) ORDER BY jt.execution_order
) as tasks
FROM workflow w
LEFT JOIN workflow_job wj ON w.id = wj.workflow_id
LEFT JOIN job j ON wj.job_id = j.id
LEFT JOIN job_task jt ON j.id = jt.job_id
LEFT JOIN task t ON jt.task_id = t.id
WHERE w.id = #{workflowId}
AND t.id IS NOT NULL -- Task가 있는 Job만 조회
GROUP BY
w.id, w.name, w.description, w.is_enabled, w.default_config,
wj.execution_order, j.id, j.name, j.description, j.is_enabled
ORDER BY wj.execution_order
</select>
</mapper>
11 changes: 7 additions & 4 deletions apps/user-service/src/main/resources/sql/03-insert-workflow.sql
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,13 @@ DELETE FROM `workflow`;
-- ===================================================================

-- 워크플로우 생성 (ID: 1)
INSERT INTO `workflow` (`id`, `name`, `description`, `created_by`) VALUES
(1, '상품 분석 및 블로그 자동 발행', '키워드 검색부터 상품 분석 후 블로그 발행까지의 자동화 프로세스', 1)
ON DUPLICATE KEY UPDATE name = VALUES(name), description = VALUES(description), updated_at = NOW();

INSERT INTO `workflow` (`id`, `name`, `description`, `created_by`, `default_config`) VALUES
(1, '상품 분석 및 블로그 자동 발행', '키워드 검색부터 상품 분석 후 블로그 발행까지의 자동화 프로세스', 1,
JSON_OBJECT('keyword_search',json_object('tag','naver'),'blog_publish',json_object('tag','naver_blog','blog_id', 'wtecho331', 'blog_pw', 'testpass')))
ON DUPLICATE KEY UPDATE
name = VALUES(name),
description = VALUES(description),
updated_at = NOW();
-- Job 생성 (ID: 1, 2)
INSERT INTO `job` (`id`, `name`, `description`, `created_by`) VALUES
(1, '상품 분석', '키워드 검색, 상품 크롤링 및 유사도 분석 작업', 1),
Expand Down
Loading