ありがとうございます。
ここから 第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
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担当。
| @Mapper
public interface TeacherDashboardMapper {
TeacherDashboardDto findDashboard(
Long userId
);
}
|
10. 教材一覧画面
URL:
検索条件:
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
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. 修正依頼
管理者:
追加情報:
例:
| コメント:
・動画説明文を追加してください
・サンプルコードを修正してください
|
承認履歴とは別にコメント機能を追加します。
理由:
レビュー議論を残すため。
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説明追加
|
比較対象:
| 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
表示:
| 削除
- 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設計方針に合わせます。
対象:
| @Auditable
public void approve(){
}
|
27. Auditable Annotation
| @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. 利用例
| @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段階 では、システム品質を高めるための 共通基盤・例外処理・バリデーション・通知設計 に進みます。
内容:
-
@ControllerAdvice例外処理
-
エラー画面設計
-
独自Exception設計
-
Bean Validation強化
-
メッセージ管理
-
メール通知設計
-
非同期処理
-
ログ設計
へ進みます。