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
4 changes: 4 additions & 0 deletions scripts/migration/add_exam_participants_code_snapshot.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- exam_participants: 시험 종료 시 자동 제출용 코드 스냅샷 (ddl-auto=update 미사용 환경용)
ALTER TABLE exam_participants
ADD COLUMN IF NOT EXISTS last_code_lang VARCHAR(50),
ADD COLUMN IF NOT EXISTS last_code_inline TEXT;
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
-- platform_settings.auto_delete_expired_data: 기존 테이블에 컬럼 추가
-- (테이블은 있으나 컬럼만 없는 로컬/운영 DB용, Hibernate ddl-auto=update 미반영 환경)
-- 적용: psql ... -v ON_ERROR_STOP=1 -f alter_platform_settings_add_auto_delete_expired_data.sql
-- 스키마 사용 시: SET search_path TO ai_vibe_coding_test; 후 실행하거나 동일 스키마에서 실행

ALTER TABLE platform_settings
ADD COLUMN IF NOT EXISTS auto_delete_expired_data BOOLEAN NOT NULL DEFAULT TRUE;

-- 컬럼이 nullable로만 추가된 구버전 대비 (재실행 안전)
UPDATE platform_settings
SET auto_delete_expired_data = TRUE
WHERE auto_delete_expired_data IS NULL;
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
-- platform_settings 보관 기간 컬럼: Hibernate ddl-auto로 생성된 불완전 테이블 보완
ALTER TABLE platform_settings
ADD COLUMN IF NOT EXISTS log_retention_days INTEGER NOT NULL DEFAULT 90;

ALTER TABLE platform_settings
ADD COLUMN IF NOT EXISTS submission_retention_days INTEGER NOT NULL DEFAULT 90;

UPDATE platform_settings
SET log_retention_days = 90
WHERE log_retention_days IS NULL;

UPDATE platform_settings
SET submission_retention_days = 90
WHERE submission_retention_days IS NULL;
22 changes: 22 additions & 0 deletions scripts/migration/create_admin_activity_logs.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
-- admin_activity_logs: 관리자 활동 로그 (Hibernate ddl-auto=update 미사용 환경용)
CREATE TABLE IF NOT EXISTS admin_activity_logs (
id BIGSERIAL PRIMARY KEY,
admin_id BIGINT NOT NULL,
exam_id BIGINT NOT NULL,
participant_id BIGINT NULL,
type VARCHAR(50) NOT NULL,
title VARCHAR(200) NOT NULL,
message VARCHAR(500) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NULL,
deleted_at TIMESTAMP NULL
);

CREATE INDEX IF NOT EXISTS idx_admin_activity_logs_admin_created
ON admin_activity_logs (admin_id, created_at DESC);

CREATE INDEX IF NOT EXISTS idx_admin_activity_logs_type
ON admin_activity_logs (type);

CREATE INDEX IF NOT EXISTS idx_admin_activity_logs_exam_id
ON admin_activity_logs (exam_id);
21 changes: 21 additions & 0 deletions scripts/migration/create_master_activity_logs.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
-- master_activity_logs: 마스터 활동 로그 (Hibernate ddl-auto=update 미사용 환경용)
CREATE TABLE IF NOT EXISTS master_activity_logs (
id BIGSERIAL PRIMARY KEY,
master_id BIGINT NULL,
target_admin_id BIGINT NULL,
type VARCHAR(50) NOT NULL,
title VARCHAR(200) NOT NULL,
message VARCHAR(500) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NULL,
deleted_at TIMESTAMP NULL
);

CREATE INDEX IF NOT EXISTS idx_master_activity_logs_master_created
ON master_activity_logs (master_id, created_at DESC);

CREATE INDEX IF NOT EXISTS idx_master_activity_logs_type
ON master_activity_logs (type);

CREATE INDEX IF NOT EXISTS idx_master_activity_logs_created
ON master_activity_logs (created_at DESC);
22 changes: 22 additions & 0 deletions scripts/migration/create_platform_settings.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
-- platform_settings: 마스터 전역 플랫폼 설정 (단일 row, id=1)
CREATE TABLE IF NOT EXISTS platform_settings (
id BIGINT PRIMARY KEY DEFAULT 1,
default_token_limit INTEGER NOT NULL,
log_retention_days INTEGER NOT NULL,
submission_retention_days INTEGER NOT NULL,
auto_delete_expired_data BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NULL,
deleted_at TIMESTAMP NULL,
CONSTRAINT chk_platform_settings_singleton CHECK (id = 1)
);

INSERT INTO platform_settings (
id,
default_token_limit,
log_retention_days,
submission_retention_days,
auto_delete_expired_data
)
VALUES (1, 10000, 90, 90, TRUE)
ON CONFLICT (id) DO NOTHING;
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.yd.vibecode.domain.admin.application.dto.request;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;

public record UpdateMasterPlatformSettingsRequest(
@NotNull(message = "로그 보관 기간은 필수입니다.")
@Min(value = 1, message = "로그 보관 기간은 1일 이상이어야 합니다.")
@Max(value = 3650, message = "로그 보관 기간이 너무 깁니다.")
@Schema(description = "로그 보관 기간(일)", example = "90")
Integer logRetentionDays,

@NotNull(message = "제출물 보관 기간은 필수입니다.")
@Min(value = 1, message = "제출물 보관 기간은 1일 이상이어야 합니다.")
@Max(value = 3650, message = "제출물 보관 기간이 너무 깁니다.")
@Schema(description = "제출물 보관 기간(일)", example = "90")
Integer submissionRetentionDays,

@NotNull(message = "만료 데이터 자동 삭제 여부는 필수입니다.")
@Schema(description = "만료 데이터 자동 삭제 활성화 여부 (false면 향후 삭제 스케줄러 미실행 정책)", example = "true")
Boolean autoDeleteExpiredData
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.yd.vibecode.domain.admin.application.dto.request;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;

@Schema(description = "문제 사용 가능 여부 변경 요청")
public record UpdateProblemAvailabilityRequest(
@Schema(description = "사용 가능 여부 (true=PUBLISHED, false=ARCHIVED)", example = "true")
@NotNull(message = "available 값은 필수입니다.")
Boolean available
) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.yd.vibecode.domain.admin.application.dto.response;

import java.util.List;

import org.springframework.data.domain.Page;

import com.yd.vibecode.domain.admin.domain.entity.AdminActivityLog;

public record AdminActivityLogPageResponse(
List<AdminActivityLogResponse> content,
int page,
int size,
long totalElements,
int totalPages
) {
public static AdminActivityLogPageResponse from(Page<AdminActivityLog> page) {
return new AdminActivityLogPageResponse(
page.getContent().stream().map(AdminActivityLogResponse::from).toList(),
page.getNumber(),
page.getSize(),
page.getTotalElements(),
page.getTotalPages()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.yd.vibecode.domain.admin.application.dto.response;

import java.time.LocalDateTime;

import com.yd.vibecode.domain.admin.domain.entity.AdminActivityLog;
import com.yd.vibecode.domain.admin.domain.entity.AdminActivityLogType;

public record AdminActivityLogResponse(
Long id,
AdminActivityLogType type,
String title,
String message,
LocalDateTime createdAt,
Long examId,
Long participantId
) {
public static AdminActivityLogResponse from(AdminActivityLog log) {
return new AdminActivityLogResponse(
log.getId(),
log.getType(),
log.getTitle(),
log.getMessage(),
log.getCreatedAt(),
log.getExamId(),
log.getParticipantId()
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ public record ExamineeBoardResponse(
BigDecimal correctnessScore,
BigDecimal totalScore,
LocalDateTime submittedAt,
LocalDateTime evaluatedAt
LocalDateTime evaluatedAt,
ParticipantAttendanceStatus attendanceStatus,
ParticipantSubmissionDisplayStatus submissionDisplayStatus
) {
public static ExamineeBoardResponse of(
ExamParticipant ep,
Expand All @@ -34,7 +36,9 @@ public static ExamineeBoardResponse of(
BigDecimal correctnessScore,
BigDecimal totalScore,
LocalDateTime submittedAt,
LocalDateTime evaluatedAt) {
LocalDateTime evaluatedAt,
ParticipantAttendanceStatus attendanceStatus,
ParticipantSubmissionDisplayStatus submissionDisplayStatus) {
return new ExamineeBoardResponse(
ep.getId(),
p != null ? p.getName() : "",
Expand All @@ -50,13 +54,14 @@ public static ExamineeBoardResponse of(
correctnessScore,
totalScore,
submittedAt,
evaluatedAt
evaluatedAt,
attendanceStatus,
submissionDisplayStatus
);
}

private static String maskPhone(String phone) {
if (phone == null || phone.length() < 9) return phone;
// 010-1234-5678 -> 010-****-5678
String[] parts = phone.split("-");
if (parts.length == 3) {
return parts[0] + "-****-" + parts[2];
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.yd.vibecode.domain.admin.application.dto.response;

import java.util.List;

import org.springframework.data.domain.Page;

import com.yd.vibecode.domain.admin.domain.entity.MasterActivityLog;

public record MasterActivityLogPageResponse(
List<MasterActivityLogResponse> content,
int page,
int size,
long totalElements,
int totalPages
) {
public static MasterActivityLogPageResponse from(Page<MasterActivityLog> page) {
return new MasterActivityLogPageResponse(
page.getContent().stream().map(MasterActivityLogResponse::from).toList(),
page.getNumber(),
page.getSize(),
page.getTotalElements(),
page.getTotalPages()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.yd.vibecode.domain.admin.application.dto.response;

import java.time.LocalDateTime;

import com.yd.vibecode.domain.admin.domain.entity.MasterActivityLog;
import com.yd.vibecode.domain.admin.domain.entity.MasterActivityLogType;

public record MasterActivityLogResponse(
Long id,
MasterActivityLogType type,
String title,
String message,
LocalDateTime createdAt,
Long targetAdminId
) {
public static MasterActivityLogResponse from(MasterActivityLog log) {
return new MasterActivityLogResponse(
log.getId(),
log.getType(),
log.getTitle(),
log.getMessage(),
log.getCreatedAt(),
log.getTargetAdminId()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.yd.vibecode.domain.admin.application.dto.response;

import java.time.LocalDateTime;

import com.yd.vibecode.domain.admin.domain.entity.PlatformSettings;

public record MasterPlatformSettingsResponse(
int logRetentionDays,
int submissionRetentionDays,
boolean autoDeleteExpiredData,
LocalDateTime updatedAt
) {
public static MasterPlatformSettingsResponse from(PlatformSettings settings) {
return new MasterPlatformSettingsResponse(
settings.getLogRetentionDays(),
settings.getSubmissionRetentionDays(),
Boolean.TRUE.equals(settings.getAutoDeleteExpiredData()),
settings.getUpdatedAt() != null ? settings.getUpdatedAt() : settings.getCreatedAt()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.yd.vibecode.domain.admin.application.dto.response;

/**
* 관리자 참가자 목록 — 응시상태 (4값)
*/
public enum ParticipantAttendanceStatus {
WAITING,
IN_EXAM,
SUBMITTED,
ENDED
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.yd.vibecode.domain.admin.application.dto.response;

/**
* 관리자 참가자 목록 — 제출상태 (3값)
*/
public enum ParticipantSubmissionDisplayStatus {
NOT_SUBMITTED,
GRADING,
GRADED
}
Loading
Loading