Skip to content
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
5094fcb
feat: 관리자 엔티티 구현
1winhyun Dec 27, 2025
3b6eb63
feat: 관리자 관련 jwt 추가
1winhyun Dec 27, 2025
13fcbf8
feat: 관리자 로그인 기능 구현
1winhyun Dec 27, 2025
0c6739f
refactor: 관리자 로그인 기능 PermitUrlConfig 추가
1winhyun Dec 27, 2025
d8f6778
refactor: conflict 해결
1winhyun Jan 4, 2026
f628d13
feat: conflict(충돌) 해결
1winhyun Jan 4, 2026
95e55d6
Merge remote-tracking branch 'origin/refactor/#22' into refactor/#22
1winhyun Jan 4, 2026
bfddc06
refactor: CurrentManagerId 어노테이션 분리
1winhyun Jan 4, 2026
2bfd2b5
Merge remote-tracking branch 'origin/dev' into refactor/#22
1winhyun Jan 4, 2026
687c944
refactor: WebMvcConfig에 CurrentManagerIdArgumentResolver 추가
1winhyun Jan 4, 2026
2367375
refactor: StudentCouncil 관리자 승인 여부 컬럼 추가
1winhyun Jan 4, 2026
5d49fec
refactor: 학생회 회원가입 시 토큰 미발급되며 학생회 인증 여부는 false가 되도록 수정
1winhyun Jan 4, 2026
8a3d957
feat: 학생회 회원가입 요청 시 관리자 인증 및 결과 메일 전송 로직 구현
1winhyun Jan 4, 2026
1d7dfdf
refactor: 학생회 대표자 로그인 및 전용 api의 경우 학생회 조회 로직에 관리자 승인 받은 상태 조건 추가
1winhyun Jan 4, 2026
b76da83
feat: StudentCouncil에 councilName 컬럼 추가 및 학생회 이름 생성 로직 구현
1winhyun Jan 5, 2026
5126e4d
refactor: StudentCouncilSignUpRequest에 당선 사진 추가
1winhyun Jan 5, 2026
53fb30e
feat: 학생회 대표자 회원가입 요청 목록 조회 기능 구현
1winhyun Jan 5, 2026
cda307d
refactor: manager 관련 에러코드 변경
1winhyun Jan 5, 2026
49ae65d
refactor: 학생회가 작성하는 글의 업데이트, 삭제 기능 승인되지 않은 학생회는 불가능하도록 승인상태 확인 추가
1winhyun Jan 5, 2026
811b700
refactor: manager의 PasswordNotCorrectException import 경로 수정
1winhyun Jan 5, 2026
e07bc01
refactor: managerApprove 메서드명 오타 수정
1winhyun Jan 5, 2026
dbd9496
refactor: getCertifyRequestCouncil 승인 대기중인 학생회만 조회되도록 수정
1winhyun Jan 5, 2026
685b4be
refactor: PermitUrlConfig 중복 제거
1winhyun Jan 5, 2026
1284fa8
refactor: StudentCouncilSignUpRequest 당선 사진 @NotBlank 추가
1winhyun Jan 5, 2026
e9f06f9
refactor: managerApproved 컬럼 null이 될 수 없도록 설정
1winhyun Jan 5, 2026
2b698dd
refactor: approveOrDenyCouncil에서 학생회의 경우 미승인 학생회만 조회되도록 수정
1winhyun Jan 5, 2026
5f71d89
refactor: 학생회 회원가입 id 중복 검증 api 구현
1winhyun Jan 6, 2026
cebfdb1
refactor: setCouncilName을 generateCouncilName으로 메서드명 수정
1winhyun Jan 6, 2026
7e65f62
refactor: validateLoginId 앤드포인트 수정
1winhyun Jan 6, 2026
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
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ public record StudentCouncilSignUpRequest(
Long collegeId,

@Schema(description = "학과 id", example = "1")
Long majorId
Long majorId,

@Schema(description = "당선 사진 url", example = "https://www.election.com.png")
String electionImageUrl
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ public record StudentCouncilLoginResponse(
String collegeName,

@Schema(description = "학과 이름 (없으면 null)", example = "컴퓨터공학과")
String majorName
String majorName,

@Schema(description = "학생회 이름", example = "가천대학교 총학생회")
String councilName
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ public StudentCouncil createStudentCouncil(StudentCouncilSignUpRequest studentCo
.school(school)
.college(college)
.major(major)
.electionImageUrl(studentCouncilSignUpRequest.electionImageUrl())
.managerApproved(false)
.build();
}

Expand All @@ -44,7 +46,8 @@ public StudentCouncilLoginResponse toStudentCouncilLoginResponse(StudentCouncil
studentCouncil.getCouncilType(),
studentCouncil.getSchool().getSchoolName(),
studentCouncil.getCollege() != null ? studentCouncil.getCollege().getCollegeName() : null,
studentCouncil.getMajor() != null ? studentCouncil.getMajor().getMajorName() : null
studentCouncil.getMajor() != null ? studentCouncil.getMajor().getMajorName() : null,
studentCouncil.getCouncilName()
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import com.campus.campus.domain.council.application.exception.SignupEmailNotFoundException;
import com.campus.campus.domain.council.application.exception.StudentCouncilNotFoundException;
import com.campus.campus.domain.council.application.mapper.StudentCouncilLoginMapper;
import com.campus.campus.domain.council.application.util.CouncilNameGenerator;
import com.campus.campus.domain.council.domain.entity.StudentCouncil;
import com.campus.campus.domain.council.domain.repository.StudentCouncilRepository;
import com.campus.campus.domain.mail.application.exception.EmailVerificationNotFoundException;
Expand Down Expand Up @@ -60,13 +61,14 @@ public class CouncilLoginService {
private final SecurityConfig securityConfig;
private final PasswordEncoder passwordEncoder;
private final RedisTokenService redisTokenService;
private final CouncilNameGenerator councilNameGenerator;

@Value("${jwt.refresh.expiration-seconds}")
private long refreshTokenExpirationSeconds;

@Transactional
public StudentCouncilLoginResponse signUp(StudentCouncilSignUpRequest studentCouncilSignUpRequest) {
if(studentCouncilRepository.existsByEmailAndDeletedAtIsNotNull(studentCouncilSignUpRequest.email())){
public void signUp(StudentCouncilSignUpRequest studentCouncilSignUpRequest) {
if (studentCouncilRepository.existsByEmailAndDeletedAtIsNotNull(studentCouncilSignUpRequest.email())) {
throw new CouncilSignupForbiddenException();
}
if (studentCouncilRepository.existsByEmail(studentCouncilSignUpRequest.email())) {
Expand All @@ -88,22 +90,17 @@ public StudentCouncilLoginResponse signUp(StudentCouncilSignUpRequest studentCou
StudentCouncil studentCouncil = studentCouncilLoginMapper.createStudentCouncil(
studentCouncilSignUpRequest, school, scope.college, scope.major);

studentCouncilRepository.save(studentCouncil);

String accessToken = jwtProvider.createCouncilAccessToken(studentCouncil.getId());
String refreshToken = jwtProvider.createCouncilRefreshToken(studentCouncil.getId());
String councilName = councilNameGenerator.buildCouncilName(studentCouncil);
studentCouncil.setCouncilName(councilName);

redisTokenService.setRefreshToken("COUNCIL", String.valueOf(studentCouncil.getId()), refreshToken,
refreshTokenExpirationSeconds);
studentCouncilRepository.save(studentCouncil);

emailVerification.use();

return studentCouncilLoginMapper.toStudentCouncilLoginResponse(studentCouncil, accessToken, refreshToken);
}

public StudentCouncilLoginResponse login(StudentCouncilLoginRequest studentCouncilLoginRequest) {
StudentCouncil studentCouncil = studentCouncilRepository
.findByLoginIdAndDeletedAtIsNull(studentCouncilLoginRequest.loginId())
.findByLoginIdAndManagerApprovedIsTrueAndDeletedAtIsNull(studentCouncilLoginRequest.loginId())
.orElseThrow(StudentCouncilNotFoundException::new);

if (!passwordEncoder.matches(studentCouncilLoginRequest.password(), studentCouncil.getPassword())) {
Expand All @@ -127,7 +124,8 @@ public StudentCouncilFindIdResponse findId(String email) {
throw new SignupEmailNotFoundException();
}

StudentCouncil studentCouncil = studentCouncilRepository.findByEmailAndDeletedAtIsNull(email)
StudentCouncil studentCouncil = studentCouncilRepository
.findByEmailAndManagerApprovedIsTrueAndDeletedAtIsNull(email)
.orElseThrow(StudentCouncilNotFoundException::new);

emailVerification.use();
Expand All @@ -138,7 +136,7 @@ public StudentCouncilFindIdResponse findId(String email) {
@Transactional
public void findPassword(StudentCouncilFindPasswordRequest studentCouncilFindPasswordRequest) {
StudentCouncil studentCouncil = studentCouncilRepository
.findByLoginIdAndDeletedAtIsNull(studentCouncilFindPasswordRequest.loginId())
.findByLoginIdAndManagerApprovedIsTrueAndDeletedAtIsNull(studentCouncilFindPasswordRequest.loginId())
.orElseThrow(StudentCouncilNotFoundException::new);

if (!studentCouncilFindPasswordRequest.email().equals(studentCouncil.getEmail())) {
Expand All @@ -157,7 +155,8 @@ public void findPassword(StudentCouncilFindPasswordRequest studentCouncilFindPas

@Transactional
public void withdrawCouncil(Long councilId, StudentCouncilWithdrawRequest studentCouncilWithdrawRequest) {
StudentCouncil studentCouncil = studentCouncilRepository.findByIdAndDeletedAtIsNull(councilId)
StudentCouncil studentCouncil = studentCouncilRepository
.findByIdAndManagerApprovedIsTrueAndDeletedAtIsNull(councilId)
.orElseThrow(StudentCouncilNotFoundException::new);

if (!studentCouncilWithdrawRequest.precaution()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ public class CouncilService {

@Transactional
public void changeEmail(Long councilId, StudentCouncilChangeEmailRequest studentCouncilChangeEmailRequest) {
StudentCouncil studentCouncil = studentCouncilRepository.findByIdAndDeletedAtIsNull(councilId)
StudentCouncil studentCouncil = studentCouncilRepository
.findByIdAndManagerApprovedIsTrueAndDeletedAtIsNull(councilId)
.orElseThrow(StudentCouncilNotFoundException::new);

//soft delete된 유저가 예상치 못하게 계정을 복구해야할 수도 있기에 이를 막기 위해 deleteAt이 존재하더라도 조회되게 한다.
Expand All @@ -50,7 +51,8 @@ public void changeEmail(Long councilId, StudentCouncilChangeEmailRequest student
@Transactional
public void changePassword(Long councilId,
StudentCouncilChangePasswordRequest studentCouncilChangePasswordRequest) {
StudentCouncil studentCouncil = studentCouncilRepository.findByIdAndDeletedAtIsNull(councilId)
StudentCouncil studentCouncil = studentCouncilRepository
.findByIdAndManagerApprovedIsTrueAndDeletedAtIsNull(councilId)
.orElseThrow(StudentCouncilNotFoundException::new);

if (!securityConfig.passwordEncoder()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.campus.campus.domain.council.application.util;

import org.springframework.stereotype.Component;

import com.campus.campus.domain.council.domain.entity.CouncilType;
import com.campus.campus.domain.council.domain.entity.StudentCouncil;

@Component
public class CouncilNameGenerator {
public String buildCouncilName(StudentCouncil studentCouncil) {
String schoolName = studentCouncil.getSchool() != null ? studentCouncil.getSchool().getSchoolName() : "";
CouncilType councilType = studentCouncil.getCouncilType();

return switch (councilType) {
case SCHOOL_COUNCIL -> String.format("%s 총학생회", schoolName).trim();
case COLLEGE_COUNCIL -> String.format("%s %s 학생회", schoolName, getCollegeName(studentCouncil)).trim();
case MAJOR_COUNCIL -> String.format("%s %s 학생회", schoolName, getMajorName(studentCouncil)).trim();
};
Comment on lines +10 to +18
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

find . -type f -name "StudentCouncil.java" | head -5

Repository: our-campUS/our-campUS-BE

Length of output: 150


🏁 Script executed:

cat -n ./src/main/java/com/campus/campus/domain/council/domain/entity/StudentCouncil.java

Repository: our-campUS/our-campUS-BE

Length of output: 3225


councilType@NotNull 검증 추가 권장

councilType 필드는 데이터베이스 레벨에서 nullable = false 제약이 있어 NULL 값이 저장될 수 없습니다. 다만 엔티티 수준의 유효성 검증을 강화하기 위해 @NotNull 또는 @jakarta.validation.constraints.NotNull 애너테이션을 추가하는 것이 좋습니다. 이는 메모리 상태에서의 객체 생성 시에도 안전성을 보장합니다.

🤖 Prompt for AI Agents
In
@src/main/java/com/campus/campus/domain/council/application/util/CouncilNameGenerator.java
around lines 10-18, Add a NotNull validation to the councilType field on the
StudentCouncil entity to enforce non-null at the object level: annotate the
councilType field (used by buildCouncilName in CouncilNameGenerator) with
@NotNull (or @jakarta.validation.constraints.NotNull), import the appropriate
annotation, and run/adjust any tests that construct StudentCouncil instances to
provide a councilType to satisfy the new constraint.

}

private String getCollegeName(StudentCouncil studentCouncil) {
if (studentCouncil.getCollege() == null) {
return "";
}
return studentCouncil.getCollege().getCollegeName();
}

private String getMajorName(StudentCouncil studentCouncil) {
if (studentCouncil.getMajor() == null) {
return "";
}
return studentCouncil.getMajor().getMajorName();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,15 @@ public class StudentCouncil extends BaseEntity {
@JoinColumn(name = "major_id")
private Major major;

@Column(name = "council_name")
private String councilName;

@Column(name = "election_image_url")
private String electionImageUrl;

@Column(name = "manager_approved")
private Boolean managerApproved;

@Column(name = "deleted_at")
private LocalDateTime deletedAt;

Expand All @@ -73,18 +82,15 @@ public void changePassword(String newPassword) {
this.password = newPassword;
}

public String getFullCouncilName() {
StringBuilder fullName = new StringBuilder();
if (school != null)
fullName.append(school.getSchoolName());
if (college != null)
fullName.append(" ").append(college.getCollegeName());
if (major != null)
fullName.append(" ").append(major.getMajorName());
return fullName.append(" 학생회").toString().trim();
}

public void changeEmail(String newEmail) {
this.email = newEmail;
}

public void setCouncilName(String councilName) {
this.councilName = councilName;
}

public void mangerApprove() {
this.managerApproved = true;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.campus.campus.domain.council.domain.repository;

import java.util.List;
import java.util.Optional;

import org.springframework.data.jpa.repository.JpaRepository;
Expand All @@ -9,28 +10,34 @@
import com.campus.campus.domain.council.domain.entity.StudentCouncil;

public interface StudentCouncilRepository extends JpaRepository<StudentCouncil, Long> {
Optional<StudentCouncil> findByLoginIdAndDeletedAtIsNull(String loginId);
Optional<StudentCouncil> findByLoginIdAndManagerApprovedIsTrueAndDeletedAtIsNull(String loginId);

Optional<StudentCouncil> findByLoginId(String loginId);
Optional<StudentCouncil> findByEmailAndManagerApprovedIsTrueAndDeletedAtIsNull(String email);

Optional<StudentCouncil> findByEmailAndDeletedAtIsNull(String email);
@Query("SELECT sc FROM StudentCouncil sc " +
"LEFT JOIN FETCH sc.school " +
"LEFT JOIN FETCH sc.college " +
"LEFT JOIN FETCH sc.major " +
"WHERE sc.id = :councilId AND sc.deletedAt IS NULL AND sc.managerApproved IS TRUE")
Optional<StudentCouncil> findByIdWithDetailsAndManagerApprovedIsTrueAndDeletedAtIsNull(
@Param("councilId") Long councilId);

@Query("SELECT sc FROM StudentCouncil sc " +
"LEFT JOIN FETCH sc.school " +
"LEFT JOIN FETCH sc.college " +
"LEFT JOIN FETCH sc.major " +
"WHERE sc.id = :councilId AND sc.deletedAt IS NULL")
Optional<StudentCouncil> findByIdWithDetailsAndDeletedAtIsNull(@Param("councilId") Long councilId);
"WHERE sc.deletedAt IS NULL AND sc.managerApproved IS FALSE")
List<StudentCouncil> findByManagerWithDetailsApprovedIsFalseAndDeletedAtIsNull();

boolean existsByLoginId(String loginId);

boolean existsByEmail(String email);

boolean existsByEmailAndDeletedAtIsNull(String email);

Optional<StudentCouncil> findByIdAndDeletedAtIsNull(Long councilId);
Optional<StudentCouncil> findByIdAndManagerApprovedIsTrueAndDeletedAtIsNull(Long councilId);

boolean existsByIdAndDeletedAtIsNull(Long councilId);
boolean existsByIdAndManagerApprovedIsTrueAndDeletedAtIsNull(Long councilId);

boolean existsByEmailAndDeletedAtIsNotNull(String email);
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,10 @@ public class StudentCouncilLoginController {

@PostMapping("/signup")
@Operation(summary = "학생회 회원가입")
public CommonResponse<StudentCouncilLoginResponse> Signup(
@Valid @RequestBody StudentCouncilSignUpRequest studentCouncilSignUpRequest) {
StudentCouncilLoginResponse response = councilLoginService.signUp(studentCouncilSignUpRequest);
public CommonResponse<Void> Signup(@Valid @RequestBody StudentCouncilSignUpRequest studentCouncilSignUpRequest) {
councilLoginService.signUp(studentCouncilSignUpRequest);

return CommonResponse.success(StudentCouncilResponseCode.SIGNUP_SUCCESS, response);
return CommonResponse.success(StudentCouncilResponseCode.SIGNUP_REQUEST_SUCCESS);
}

@PostMapping("/login")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
@Getter
@AllArgsConstructor
public enum StudentCouncilResponseCode implements ResponseCodeInterface {
SIGNUP_SUCCESS(200, HttpStatus.OK, "학생회 회원가입에 성공했습니다."),
SIGNUP_REQUEST_SUCCESS(200, HttpStatus.OK, "학생회 회원가입 요청에 성공했습니다."),
LOGIN_SUCCESS(200, HttpStatus.OK, "학생회 로그인에 성공했습니다."),
FIND_ID_SUCCESS(200, HttpStatus.OK, "아이디 찾기에 성공했습니다."),
FIND_PASSWORD_SUCCESS(200, HttpStatus.OK, "비밀번호 재설정에 성공했습니다."),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public NoticeResponse toNoticeResponse(StudentCouncilNotice notice, List<String>
return NoticeResponse.builder()
.id(notice.getId())
.writerId(notice.getWriter().getId())
.writerName(notice.getWriter().getFullCouncilName())
.writerName(notice.getWriter().getCouncilName())
.isWriter(notice.isWrittenByCouncil(councilId))
.title(notice.getTitle())
.content(notice.getContent())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ public NoticeResponse create(Long councilId, NoticeRequest dto) {
throw new NoticeImageLimitExceededException();
}

StudentCouncil writer = studentCouncilRepository.findByIdWithDetailsAndDeletedAtIsNull(councilId)
StudentCouncil writer = studentCouncilRepository.
findByIdWithDetailsAndManagerApprovedIsTrueAndDeletedAtIsNull(councilId)
.orElseThrow(StudentCouncilNotFoundException::new);

StudentCouncilNotice notice =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public PostResponse toPostResponse(StudentCouncilPost post, List<String> images,
var builder = PostResponse.builder()
.id(post.getId())
.writerId(writer.getId())
.writerName(writer.getFullCouncilName())
.writerName(writer.getCouncilName())
.isWriter(post.isWrittenByCouncil(currentUserId))
.category(post.getCategory())
.title(post.getTitle())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ public PostResponse create(Long councilId, PostRequest dto) {
throw new PostImageLimitExceededException();
}

StudentCouncil writer = studentCouncilRepository.findByIdWithDetailsAndDeletedAtIsNull(councilId)
StudentCouncil writer = studentCouncilRepository
.findByIdWithDetailsAndManagerApprovedIsTrueAndDeletedAtIsNull(councilId)
.orElseThrow(StudentCouncilNotFoundException::new);

if (dto.thumbnailImageUrl() == null && dto.thumbnailIcon() == null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.campus.campus.domain.manager.application.dto.request;

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

public record CouncilApproveOrDenyRequest(
@NotNull
@Schema(description = "인증 결과", example = "true")
boolean certifyResult
) {
Comment on lines +6 to +10
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

@NotNull이 primitive boolean에는 효과가 없습니다

primitive boolean은 null이 될 수 없으므로 @NotNull 검증이 작동하지 않습니다. JSON 요청에서 certifyResult 필드가 누락되면 기본값 false로 설정되어 의도치 않은 거부 처리가 될 수 있습니다.

Boolean wrapper 타입을 사용해야 null 검증이 정상 작동합니다.

🔎 제안하는 수정
 public record CouncilApproveOrDenyRequest(
 	@NotNull
 	@Schema(description = "인증 결과", example = "true")
-	boolean certifyResult
+	Boolean certifyResult
 ) {
 }
🤖 Prompt for AI Agents
In
@src/main/java/com/campus/campus/domain/manager/application/dto/request/CouncilApproveOrDenyRequest.java
around lines 6-10, The @NotNull annotation on the primitive field in record
CouncilApproveOrDenyRequest has no effect because primitive boolean cannot be
null; change the field type certifyResult from primitive boolean to the wrapper
Boolean so validation will detect missing/null JSON values and avoid silently
defaulting to false. Update the record signature to use Boolean for
certifyResult and keep the @NotNull and @Schema annotations so the bean
validation and OpenAPI docs continue to work as intended.

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.campus.campus.domain.manager.application.dto.request;

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

public record ManagerLoginRequest(
@NotBlank
@Schema(description = "로그인 id", example = "illtathebest1")
String loginId,

@NotBlank
@Schema(description = "로그인 비밀번호", example = "illtathebest")
String password
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.campus.campus.domain.manager.application.dto.response;

import java.time.LocalDateTime;

import io.swagger.v3.oas.annotations.media.Schema;

public record CertifyRequestCouncilListResponse(
@Schema(description = "인증 요청한 학생회 id", example = "1")
Long councilId,

@Schema(description = "인증 요청한 학생회 이름", example = "가천대학교 총학생회")
String councilName,

@Schema(description = "학생회 생성시간(요청시간)", example = "2026-01-05T11:18:52.92955")
LocalDateTime createdAt
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.campus.campus.domain.manager.application.dto.response;

import io.swagger.v3.oas.annotations.media.Schema;

public record CertifyRequestCouncilResponse(
@Schema(description = "인증 요청한 학생회 id", example = "1")
Long councilId,

@Schema(description = "인증 요청한 학생회 이름", example = "가천대학교 총학생회")
String councilName,

@Schema(description = "학생회 당선 인증 사진 url", example = "https://www.example.com.png")
String electionImageUrl
) {
}
Loading