效果与 LiteFlow 类似（风格很不同）

### 1、定义组件

```java
@Component("fetchCommunity")
public class FetchCommunity implements TaskComponent {
    @Override
    public void run(FlowContext context, Node node) throws Throwable {
        context.put("communityIds", Arrays.asList("a1", "a2", "a3"));
    }
}

@Component("fetchAlbum")
public class FetchAlbum implements TaskComponent {
    @Override
    public void run(FlowContext context, Node node) throws Throwable {
        String communityId = context.get("communityId");
        context.put("albumIds", Arrays.asList(communityId + "-b1", communityId + "-b2", communityId + "-b3"));
    }
}

...
```

### 2、编排

简化结构

```yaml
id: fetch
title: "采集"
layout:
  - task: "@fetchCommunity"
  - { type: loop, id: fetch-album, meta: { "$for": "communityId", "$in": "communityIds" } }
  - task: "@fetchAlbum"
  - { type: loop, id: fetch-course,  meta: { "$for": "albumId", "$in": "albumIds" } }
  - task: "@fetchCourse"
  - { type: loop, id: fetch-content, meta: { "$for": "courseId", "$in": "courseIds" } }
  - task: "@fetchContent"
  - task: "@downloadAttachment"
  - task: "@convertAttachment"
  - task: "@markdownAttachment"
```

完整结构（带栏栅）

```yaml
id: fetch2
title: "采集（完整结构）"
layout:
  - task: "@fetchCommunity"
  - { type: loop, id: fetch-album, meta: { "$for": "communityId", "$in": "communityIds" } }
  - task: "@fetchAlbum"
  - { type: loop, id: fetch-course,  meta: { "$for": "albumId", "$in": "albumIds" } }
  - task: "@fetchCourse"
  - { type: loop, id: fetch-content, meta: { "$for": "courseId", "$in": "courseIds" } }
  - task: "@fetchContent"
  - task: "@downloadAttachment"
  - task: "@convertAttachment"
  - task: "@markdownAttachment"
  - {type: loop, id: fetch-content_end}
  - {type: loop, id: fetch-course_end}
  - {type: loop, id: fetch-album_end}
```