コンテンツにスキップ

ありがとうございます。

ここから 第25段階:教師・管理者向け運用機能設計(教材審査ワークフロー・監査ログ) に進みます。

今回は、実際の教材制作現場で必要になる 「作成 → 申請 → レビュー → 修正 → 承認 → 公開」 の運用フローを設計します。


第25段階 全体構成

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
教師

 ↓

教材作成

 ↓

下書き保存

 ↓

審査申請

 ↓

管理者レビュー

 ↓

+----------------+

| 承認            |

| 修正依頼        |

| 却下            |

+----------------+

 ↓

公開

 ↓

受講者利用

1. 教材公開ワークフロー設計

状態遷移

ContentVersionの状態を拡張します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
DRAFT
 下書き

   |
   | 申請

   v

REVIEWING
 審査中

   |
   +----------------+
   |                |
   | 承認           | 修正依頼
   |                |
   v                v

WAITING_PUBLISH    DRAFT


   |

   | 公開

   v

PUBLISHED


   |

   | 新版公開

   v

ARCHIVED

2. ContentStatus拡張

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public enum ContentStatus {


    DRAFT,

    REVIEWING,

    WAITING_PUBLISH,

    PUBLISHED,

    REJECTED,

    ARCHIVED

}

3. ApprovalHistory設計

目的

誰が、いつ、何を判断したかを記録します。

例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
教材:
Java基礎 Lesson1


2026/08/04

教師 山田

申請


2026/08/05

管理者 佐藤

修正依頼

理由:
説明不足


2026/08/06

教師 山田

修正再申請

4. ApprovalHistory Entity

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
@Entity
@Table(name="approval_histories")
@Getter
@Setter
public class ApprovalHistory {


@Id
@GeneratedValue(
strategy=GenerationType.IDENTITY
)
private Long id;


@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(
name="content_version_id"
)
private ContentVersion contentVersion;


@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(
name="user_id"
)
private User user;


@Enumerated(EnumType.STRING)
private ApprovalAction action;


@Column(
columnDefinition="TEXT"
)
private String comment;


private LocalDateTime createdAt;


}

5. ApprovalAction

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public enum ApprovalAction {


SUBMIT,

APPROVE,

REJECT,

REQUEST_REVISION,

PUBLISH

}

6. テーブル設計

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
CREATE TABLE approval_histories (

id BIGINT AUTO_INCREMENT PRIMARY KEY,

content_version_id BIGINT NOT NULL,

user_id BIGINT NOT NULL,

action VARCHAR(50),

comment TEXT,

created_at DATETIME

);

7. 教師ダッシュボード

URL:

1
/teacher/dashboard

表示内容:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
--------------------------------

こんにちは 山田先生


教材状況


下書き       12件

審査中        3件

公開中       25件


最近更新した教材


・Java入門
・Spring Boot基礎


--------------------------------

8. TeacherDashboardDto

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
@Data
public class TeacherDashboardDto {


private Integer draftCount;


private Integer reviewingCount;


private Integer publishedCount;


private List<ContentSummaryDto> recentContents;


}

9. TeacherDashboardMapper

集計はMyBatis担当。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@Mapper
public interface TeacherDashboardMapper {


TeacherDashboardDto findDashboard(
Long userId
);


}

10. 教材一覧画面

URL:

1
/teacher/content

検索条件:

1
2
3
4
5
6
7
タイトル

教材種類

状態

更新日

11. ContentSearchCondition

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Data
public class ContentSearchCondition {


private Long authorId;


private String keyword;


private ContentType contentType;


private ContentStatus status;


private LocalDate from;


private LocalDate to;


}

12. Content一覧DTO

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Data
public class ContentListDto {


private Long id;


private String title;


private String type;


private String status;


private String author;


private LocalDateTime updatedAt;


}

13. 管理者審査画面

URL:

1
/admin/approval

一覧:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
------------------------------------------------

教材審査一覧


Java入門

作成者:
山田


申請日:
2026/08/04


[確認]


------------------------------------------------


Spring Boot

作成者:
田中


[確認]

------------------------------------------------

14. ApprovalController

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@Controller
@RequestMapping("/admin/approval")
@RequiredArgsConstructor
public class ApprovalController {


private final ApprovalService service;



@GetMapping
public String index(
Model model){


model.addAttribute(
"contents",
service.findReviewing()
);


return
"admin/approval/index";

}


}

15. 承認処理

流れ:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
管理者

↓

承認ボタン

↓

ContentVersion更新

↓

ApprovalHistory保存

↓

公開可能状態

16. ApprovalService

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
@Service
@RequiredArgsConstructor
public class ApprovalService {


private final ContentVersionRepository repository;


private final ApprovalHistoryRepository historyRepository;



@Transactional
public void approve(
Long versionId,
User admin){


ContentVersion version =
repository
.findById(versionId)
.orElseThrow();


version.setStatus(
ContentStatus.WAITING_PUBLISH
);


ApprovalHistory history =
new ApprovalHistory();


history.setContentVersion(
version
);


history.setUser(
admin
);


history.setAction(
ApprovalAction.APPROVE
);


historyRepository.save(
history
);


}

}

17. 修正依頼

管理者:

1
2
3
4
5
6
7
8
9
修正依頼

↓

教師へ通知

↓

DRAFTへ戻す

追加情報:

1
private String comment;

例:

1
2
3
4
コメント:

・動画説明文を追加してください
・サンプルコードを修正してください

18. ContentComment設計

承認履歴とは別にコメント機能を追加します。

理由:

レビュー議論を残すため。


Entity

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@Entity
@Table(name="content_comments")
@Getter
@Setter
public class ContentComment {


@Id
@GeneratedValue
private Long id;


@ManyToOne
private ContentVersion contentVersion;


@ManyToOne
private User user;


@Column(
columnDefinition="TEXT"
)
private String message;


private LocalDateTime createdAt;


}

19. コメントテーブル

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
CREATE TABLE content_comments (

id BIGINT AUTO_INCREMENT PRIMARY KEY,

content_version_id BIGINT,

user_id BIGINT,

message TEXT,

created_at DATETIME

);

20. Version比較

教材更新時に重要です。

例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
Version 1

Javaとは


↓

Version 2

Javaとは

+
JVM説明追加

比較対象:

1
2
3
4
5
6
7
8
9
title

summary

text body

quiz

attachment

21. ContentDiffDto

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
@Data
public class ContentDiffDto {


private String before;


private String after;


private String changeType;


}

22. 差分表示

利用候補:

  • diff-match-patch

  • Java Diff Utils


表示:

1
2
3
4
5
6
7
8
削除

- Javaは1995年に登場


追加

+ Javaはオブジェクト指向言語です

23. 監査ログ設計

教材操作以外も管理します。

対象:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
ログイン

教材登録

教材変更

承認

公開

削除

権限変更

24. AuditLog Entity

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@Entity
@Table(name="audit_logs")
@Getter
@Setter
public class AuditLog {


@Id
@GeneratedValue
private Long id;


@ManyToOne
private User user;


private String action;


private String targetType;


private Long targetId;


@Column(
columnDefinition="TEXT"
)
private String detail;


private LocalDateTime createdAt;


}

25. AuditLogテーブル

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
CREATE TABLE audit_logs (

id BIGINT AUTO_INCREMENT PRIMARY KEY,

user_id BIGINT,

action VARCHAR(100),

target_type VARCHAR(100),

target_id BIGINT,

detail TEXT,

created_at DATETIME

);

26. Spring AOPによる自動記録

以前のSpring設計方針に合わせます。

対象:

1
2
3
4
@Auditable
public void approve(){

}

27. Auditable Annotation

1
2
3
4
5
6
7
8
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Auditable {


String value();

}

28. AuditAspect

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@Aspect
@Component
@RequiredArgsConstructor
public class AuditAspect {


private final AuditLogService service;



@Around(
"@annotation(auditable)"
)
public Object execute(
ProceedingJoinPoint joinPoint,
Auditable auditable)
throws Throwable {


Object result =
joinPoint.proceed();


service.save(
auditable.value()
);


return result;


}

}

29. 利用例

1
2
3
4
5
6
7
@Auditable(
"CONTENT_APPROVE"
)
@Transactional
public void approve(){

}

30. 管理者機能一覧

完成:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
/admin

├ dashboard

├ users

├ approval

├ contents

├ audit

└ settings

31. 第25段階終了時点

実装可能:

✅ 教師ダッシュボード
✅ 教材一覧
✅ 教材検索
✅ 審査ワークフロー
✅ 承認処理
✅ 修正依頼
✅ コメント機能
✅ Version比較
✅ Approval履歴
✅ 監査ログ
✅ Spring AOPログ記録基盤


次の 第26段階 では、システム品質を高めるための 共通基盤・例外処理・バリデーション・通知設計 に進みます。

内容:

  1. @ControllerAdvice例外処理

  2. エラー画面設計

  3. 独自Exception設計

  4. Bean Validation強化

  5. メッセージ管理

  6. メール通知設計

  7. 非同期処理

  8. ログ設計

へ進みます。