Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
594407e
Merge pull request #130 from pirogramming/main
qkrxogmla May 19, 2025
ccf4b05
Merge pull request #132 from pirogramming/main
qkrxogmla May 19, 2025
db22d12
Merge pull request #135 from pirogramming/backend
dietken1 May 20, 2025
861a5e4
[fix]: ManageStudent.jsx css 수정
NamKyeongMin May 21, 2025
296161b
[add]: DetailManageStudent.jsx 구현을 위한 경로, api 추가
NamKyeongMin May 21, 2025
a71e880
Merge pull request #136 from pirogramming/frontend_km
NamKyeongMin May 21, 2025
08242a5
Merge pull request #137 from pirogramming/main
NamKyeongMin May 21, 2025
c7f6dd8
수정 전 커밋...
Imggaggu May 21, 2025
919e41f
[Feat] merge deploy into frontend_admin_sj
Imggaggu May 21, 2025
4b35e53
[Feat] admin attendance student api연결
Imggaggu May 21, 2025
cfef8c8
[fix]: DetailManageStudent.jsx 데이터 불러오는 로직 수정
NamKyeongMin May 21, 2025
2dd8f9e
Merge pull request #138 from pirogramming/frontend_km
NamKyeongMin May 21, 2025
b399b35
[Fix] assignment.js api 재연결
Imggaggu May 21, 2025
4823994
Merge pull request #139 from pirogramming/frontend_admin_sj
Imggaggu May 21, 2025
94a8a52
[add]: Detail_greenbox UI 추가
NamKyeongMin May 21, 2025
b3e97aa
새로고침이나 페이지 진입시 기존 출석코드 종료
qkrxogmla May 21, 2025
9be2627
새로고침이나 페이지 진입시 기존 출석코드 종료
qkrxogmla May 21, 2025
ab6bce1
[add]: move_detail_to_attendance btn 추가
NamKyeongMin May 21, 2025
4fee064
Merge pull request #140 from pirogramming/frontend_th
qkrxogmla May 21, 2025
bafeed5
Merge pull request #141 from pirogramming/main
qkrxogmla May 21, 2025
62b9187
Merge pull request #142 from pirogramming/frontend_km
NamKyeongMin May 21, 2025
8328505
[Fix]merge& adminassignment 연결
Imggaggu May 21, 2025
28df930
Merge pull request #143 from pirogramming/frontend_admin_sj
Imggaggu May 21, 2025
4177928
Merge pull request #144 from pirogramming/main
NamKyeongMin May 21, 2025
7d7fe68
[Fix] AdminStudentAssignment.jsx
Imggaggu May 21, 2025
c98f542
[fix]: manage-students-controller api에서 보내는 student id 확인중
NamKyeongMin May 21, 2025
e09a6c8
Merge pull request #145 from pirogramming/frontend_km
NamKyeongMin May 21, 2025
24847b2
Merge pull request #146 from pirogramming/main
NamKyeongMin May 21, 2025
135faeb
Update vite.config.js
Imggaggu May 21, 2025
d1fb9ec
Update vite.config.js
Imggaggu May 21, 2025
5a7c2e0
Update assignment.js
Imggaggu May 21, 2025
355920d
Merge pull request #148 from pirogramming/backend
dietken1 May 21, 2025
bfc0996
Merge pull request #149 from pirogramming/main
qkrxogmla May 21, 2025
30b5a0f
[fix]: DetailManageStudent error fix
NamKyeongMin May 21, 2025
655bc4a
Merge pull request #150 from pirogramming/frontend_km
NamKyeongMin May 21, 2025
34b1d9c
인트로 이미지 경로 수정
qkrxogmla May 21, 2025
23deee4
Merge pull request #151 from pirogramming/frontend_th
qkrxogmla May 21, 2025
e12c0a9
Merge pull request #152 from pirogramming/main
qkrxogmla May 21, 2025
006fd81
[fix] managestudent
seonjuuu May 21, 2025
0357077
Merge pull request #153 from pirogramming/backend_sj
seonjuuu May 21, 2025
5dcb8c5
출석 수정
qkrxogmla May 21, 2025
9769d38
Merge pull request #154 from pirogramming/frontend_th
qkrxogmla May 21, 2025
ba00de9
Merge pull request #155 from pirogramming/main
qkrxogmla May 21, 2025
73d5b14
[add]: detailstudentmanage - 출석관리 btn + 과제 btn
NamKyeongMin May 21, 2025
f8be044
Merge pull request #156 from pirogramming/frontend_km
NamKyeongMin May 21, 2025
80b7c93
[feat] swagger update
seonjuuu May 21, 2025
5fc48e6
[fix]: Attendance.jsx api 수정
NamKyeongMin May 21, 2025
cf4c580
Merge pull request #157 from pirogramming/frontend_km
NamKyeongMin May 21, 2025
43ea002
[fix] 보증금 0원까지
seonjuuu May 21, 2025
4da49db
Merge pull request #158 from pirogramming/backend_sj
seonjuuu May 21, 2025
5d2ea51
Merge pull request #159 from pirogramming/backend_sj
seonjuuu May 21, 2025
04bd57a
[fix]: detailstudentmanage 출석/과제 버튼 로직 수정
NamKyeongMin May 21, 2025
75487bc
Merge pull request #160 from pirogramming/frontend_km
NamKyeongMin May 21, 2025
a5c9d11
[add]: 버튼 조건부 렌더링 추가
NamKyeongMin May 21, 2025
1fd7922
Merge pull request #161 from pirogramming/frontend_km
NamKyeongMin May 21, 2025
eb28023
Merge pull request #162 from pirogramming/main
NamKyeongMin May 21, 2025
6d80ef4
fix: 관리자 출석 조회 및 변경 api 수정
dietken1 May 22, 2025
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 @@ -23,135 +23,180 @@

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/admin/attendance")
@RequestMapping("/api")
@Tag(name = "관리자 출석관리", description = "관리자용 출석 관리 API")
public class AdminAttendanceController {

private final AttendanceService attendanceService;

// 출석체크 시작
@Operation(summary = "출석 체크 시작", description = "새로운 출석 코드를 생성하고 출석 체크를 시작합니다.")
@ApiResponses({
@io.swagger.v3.oas.annotations.responses.ApiResponse(
responseCode = "200",
description = "출석 코드 생성 성공",
content = @Content(schema = @Schema(implementation = AttendanceCodeResponse.class))
),
@ApiResponses(value = {
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "출석 코드 생성 성공"),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "잘못된 요청")
})
@PostMapping("/start")
public ApiResponse<AttendanceCodeResponse> startAttendance() {
@PostMapping("/admin/attendance/start")
public AttendanceCodeResponse startAttendance() {
try {
AttendanceCode code = attendanceService.generateCodeAndCreateAttendances();
return ApiResponse.success(AttendanceCodeResponse.from(code));
return AttendanceCodeResponse.from(code);
} catch (IllegalStateException e) {
// 하루 최대 출석 체크 횟수를 초과한 경우
return ApiResponse.error(e.getMessage());
throw new IllegalStateException(e.getMessage());
} catch (Exception e) {
return ApiResponse.error("출석 코드 생성 중 오류가 발생했습니다: " + e.getMessage());
throw new RuntimeException("출석 코드 생성 중 오류가 발생했습니다: " + e.getMessage());
}
}

// 현재 활성화된 출석코드 조회
@Operation(summary = "현재 활성화된 출석 코드 조회", description = "현재 활성화된 출석 코드 정보를 조회합니다.")
@ApiResponses({
@io.swagger.v3.oas.annotations.responses.ApiResponse(
responseCode = "200",
description = "조회 성공",
content = @Content(schema = @Schema(implementation = AttendanceCodeResponse.class))
),
@ApiResponses(value = {
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "조회 성공"),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "활성화된 출석 코드 없음")
})
@GetMapping("/active-code")
public ApiResponse<AttendanceCodeResponse> getActiveCode() {
@GetMapping("/admin/attendance/active-code")
public AttendanceCodeResponse getActiveCode() {
Optional<AttendanceCode> codeOpt = attendanceService.getActiveAttendanceCode();

if (codeOpt.isEmpty()) {
return ApiResponse.error("현재 활성화된 출석코드가 없습니다");
throw new RuntimeException("현재 활성화된 출석코드가 없습니다");
}

return ApiResponse.success(AttendanceCodeResponse.from(codeOpt.get()));
return AttendanceCodeResponse.from(codeOpt.get());
}

// 출석체크 종료 (코드 직접 전달)
@Operation(summary = "특정 출석 코드 만료", description = "특정 출석 코드를 만료 처리합니다.")
@ApiResponses({
@ApiResponses(value = {
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "만료 처리 성공"),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "잘못된 요청"),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "출석 코드를 찾을 수 없음")
})
@PutMapping("/expire")
public ApiResponse<Void> expireAttendance(
@Parameter(description = "만료할 출석 코드", required = true)
@PutMapping("/admin/attendance/expire")
public String expireAttendance(
@Parameter(description = "만료할 출석 코드", example = "1234")
@RequestParam String code) {
String result = attendanceService.expireAttendanceCode(code);

if (result.equals("출석 코드가 성공적으로 만료되었습니다")) {
return ApiResponse.success(result, null);
} else {
return ApiResponse.error(result);
}
return attendanceService.expireAttendanceCode(code);
}

// 출석체크 종료 (가장 최근 활성화된 코드 자동 만료)
@Operation(summary = "최근 활성화된 출석 코드 만료", description = "가장 최근 활성화된 출석 코드를 자동으로 만료 처리합니다.")
@ApiResponses({
@ApiResponses(value = {
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "만료 처리 성공"),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "활성화된 출석 코드가 없음")
})
@PutMapping("/expire-latest")
public ApiResponse<Void> expireLatestAttendance() {
String result = attendanceService.expireLatestAttendanceCode();

if (result.equals("출석 코드가 성공적으로 만료되었습니다")) {
return ApiResponse.success(result, null);
} else {
return ApiResponse.error(result);
}
@PutMapping("/admin/attendance/expire-latest")
public String expireLatestAttendance() {
return attendanceService.expireLatestAttendanceCode();
}

// 출석 상태 변경 (관리자 전용)
@Operation(summary = "출석 상태 변경", description = "관리자가 특정 사용자의 출석 상태를 변경합니다.")
@ApiResponses({
@ApiResponses(value = {
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "출석 상태 변경 성공"),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "잘못된 요청"),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "출석 기록을 찾을 수 없음")
})
@PutMapping("/status")
public ApiResponse<Void> updateAttendanceStatus(
@io.swagger.v3.oas.annotations.parameters.RequestBody(
description = "출석 상태 변경 요청",
required = true,
content = @Content(schema = @Schema(implementation = UpdateAttendanceStatusReq.class))
)
@PutMapping("/admin/users/{userId}/attendance/{attendanceId}/status")
public boolean updateAttendanceStatus(
@Parameter(description = "사용자 ID", example = "1")
@PathVariable Long userId,
@Parameter(description = "출석 ID", example = "1")
@PathVariable Long attendanceId,
@RequestBody UpdateAttendanceStatusReq req) {

boolean result = attendanceService.updateAttendanceStatus(
req.getAttendanceId(),
req.isStatus()
);
// userId 파라미터 검증은 여기서 할 수 있음 (필요 시)
return attendanceService.updateAttendanceStatus(attendanceId, req.isStatus());
}

// 출석 기록 삭제 (관리자 전용)
@Operation(summary = "출석 기록 삭제", description = "관리자가 특정 사용자의 출석 기록을 삭제합니다.")
@ApiResponses(value = {
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "출석 기록 삭제 성공"),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "잘못된 요청"),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "출석 기록을 찾을 수 없음")
})
@DeleteMapping("/admin/users/{userId}/attendance/{attendanceId}")
public boolean deleteAttendance(
@Parameter(description = "사용자 ID", example = "1")
@PathVariable Long userId,
@Parameter(description = "출석 ID", example = "1")
@PathVariable Long attendanceId) {

if (result) {
return ApiResponse.success("출석 상태가 성공적으로 변경되었습니다", null);
} else {
return ApiResponse.error("출석 상태 변경에 실패했습니다. 출석 기록을 찾을 수 없습니다.");
}
// userId 파라미터 검증은 여기서 할 수 있음 (필요 시)
return attendanceService.deleteAttendance(attendanceId);
}

// 특정 날짜와 차수에 대한 모든 학생의 출석 현황 조회
@Operation(summary = "특정 날짜와 차수의 출석 현황 조회", description = "특정 날짜와 차수에 대한 모든 학생의 출석 현황을 조회합니다.")
@ApiResponses({
@ApiResponses(value = {
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "조회 성공"),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "잘못된 요청")
})
@GetMapping("/list")
public ApiResponse<List<UserAttendanceStatusRes>> getAllAttendanceByDateAndOrder(
@Parameter(description = "조회할 날짜 (YYYY-MM-DD)", required = true)
@GetMapping("/admin/attendance/list")
public List<UserAttendanceStatusRes> getAllAttendanceByDateAndOrder(
@Parameter(description = "조회할 날짜 (YYYY-MM-DD)", example = "2023-08-01")
@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate date,
@Parameter(description = "조회할 차수", example = "1")
@RequestParam int order) {
return attendanceService.findAllByDateAndOrder(date, order);
}

// 특정 사용자의 특정 날짜와 차수 출석 기록 조회
@Operation(summary = "특정 사용자의 특정 날짜와 차수 출석 조회", description = "특정 사용자의 특정 날짜와 차수 출석 기록을 조회합니다.")
@ApiResponses(value = {
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "조회 성공"),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "잘못된 요청"),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "출석 기록을 찾을 수 없음")
})
@GetMapping("/admin/users/{userId}/attendance")
public UserAttendanceStatusRes getUserAttendanceByDateAndOrder(
@Parameter(description = "사용자 ID", example = "1")
@PathVariable Long userId,
@Parameter(description = "조회할 날짜 (YYYY-MM-DD)", example = "2023-08-01")
@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate date,
@Parameter(description = "조회할 차수", required = true)
@Parameter(description = "조회할 차수", example = "1")
@RequestParam int order) {
return attendanceService.findByUserIdAndDateAndOrder(userId, date, order);
}

// 특정 출석 ID로 출석 기록 조회
@Operation(summary = "특정 출석 기록 조회", description = "특정 학생의 특정 출석 기록을 ID로 조회합니다.")
@ApiResponses(value = {
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "조회 성공"),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "출석 기록을 찾을 수 없음")
})
@GetMapping("/admin/users/{userId}/attendance/{attendanceId}")
public UserAttendanceStatusRes getAttendanceById(
@Parameter(description = "사용자 ID", example = "1")
@PathVariable Long userId,
@Parameter(description = "출석 ID", example = "1")
@PathVariable Long attendanceId) {

List<UserAttendanceStatusRes> attendances = attendanceService.findAllByDateAndOrder(date, order);
return ApiResponse.success(attendances);
UserAttendanceStatusRes attendance = attendanceService.findById(attendanceId);

if (attendance == null) {
throw new RuntimeException("출석 기록을 찾을 수 없습니다");
}

// 요청된 userId와 조회된 출석 기록의 userId가 일치하는지 확인
if (!attendance.getUserId().equals(userId)) {
throw new RuntimeException("요청된 사용자 ID와 출석 기록의 사용자 ID가 일치하지 않습니다");
}

return attendance;
}

// 학생용 출석 현황 조회
@Operation(summary = "학생별 출석 현황 조회", description = "특정 학생의 출석 현황을 조회합니다.")
@ApiResponses(value = {
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "조회 성공"),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "잘못된 요청")
})
@GetMapping("/attendance/{userId}")
public List<UserAttendanceStatusRes> getUserAttendances(
@Parameter(description = "사용자 ID", example = "1")
@PathVariable Long userId) {
return attendanceService.findAllByUserId(userId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@
@AllArgsConstructor
@Schema(description = "출석 상태 수정 요청")
public class UpdateAttendanceStatusReq {
@Schema(description = "출석 기록 ID", example = "1")
private Long attendanceId;

@Schema(description = "변경할 출석 상태", example = "true")
private boolean status;
}
Original file line number Diff line number Diff line change
Expand Up @@ -247,4 +247,82 @@ public List<UserAttendanceStatusRes> findAllByDateAndOrder(LocalDate date, int o
.sorted(Comparator.comparing(UserAttendanceStatusRes::getUsername))
.toList();
}

// 특정 학생의 모든 출석 현황 조회
public List<UserAttendanceStatusRes> findAllByUserId(Long userId) {
// 해당 사용자의 모든 출석 기록 조회
List<Attendance> attendances = attendanceRepository.findByUserId(userId);

// DTO 변환
return attendances.stream()
.map(attendance -> {
User user = attendance.getUser();
return UserAttendanceStatusRes.builder()
.userId(user.getId())
.username(user.getName())
.date(attendance.getDate())
.order(attendance.getOrder())
.status(attendance.isStatus())
.attendanceId(attendance.getId())
.build();
})
.sorted(Comparator.comparing(UserAttendanceStatusRes::getDate).reversed()
.thenComparing(UserAttendanceStatusRes::getOrder))
.toList();
}

// 특정 사용자의 특정 출석 기록 삭제
@Transactional
public boolean deleteAttendance(Long attendanceId) {
Optional<Attendance> attendanceOpt = attendanceRepository.findById(attendanceId);

if (attendanceOpt.isEmpty()) {
return false;
}

attendanceRepository.delete(attendanceOpt.get());
return true;
}

// 특정 사용자의 특정 날짜와 차수 출석 기록 조회
public UserAttendanceStatusRes findByUserIdAndDateAndOrder(Long userId, LocalDate date, int order) {
Optional<Attendance> attendanceOpt = attendanceRepository.findByUserIdAndDateAndOrder(userId, date, order);

if (attendanceOpt.isEmpty()) {
return null;
}

Attendance attendance = attendanceOpt.get();
User user = attendance.getUser();

return UserAttendanceStatusRes.builder()
.userId(user.getId())
.username(user.getName())
.date(attendance.getDate())
.order(attendance.getOrder())
.status(attendance.isStatus())
.attendanceId(attendance.getId())
.build();
}

// 특정 출석 ID로 출석 기록 조회
public UserAttendanceStatusRes findById(Long attendanceId) {
Optional<Attendance> attendanceOpt = attendanceRepository.findById(attendanceId);

if (attendanceOpt.isEmpty()) {
return null;
}

Attendance attendance = attendanceOpt.get();
User user = attendance.getUser();

return UserAttendanceStatusRes.builder()
.userId(user.getId())
.username(user.getName())
.date(attendance.getDate())
.order(attendance.getOrder())
.status(attendance.isStatus())
.attendanceId(attendance.getId())
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,23 @@

import backend.pirocheck.Deposit.dto.DepositResDto;
import backend.pirocheck.Deposit.service.DepositService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Tag(name = "보증금 관리", description = "수강생 보증금/차감/방어권 관련 API")
@RestController
@RequestMapping("/api/deposit")
@RequiredArgsConstructor
public class DepositController {

private final DepositService depositService;

@Operation(summary = "보증금 조회", description = "해당 유저의 현재 보증금, 차감 내역, 방어권 금액을 반환합니다.")
@GetMapping("/{userId}")
public DepositResDto getDeposit(@PathVariable Long userId) {
return depositService.getDeposit(userId);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package backend.pirocheck.Deposit.dto;


import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
Expand All @@ -11,9 +12,16 @@
@NoArgsConstructor
@AllArgsConstructor
public class DepositResDto {
@Schema(description = "현재 보증금 잔액", example = "110000")
private int amount;

@Schema(description = "과제 차감 총액", example = "10000")
private int descentAssignment;

@Schema(description = "출석 차감 총액", example = "10000")
private int descentAttendance;

@Schema(description = "방어권 보상 총액", example = "10000")
private int ascentDefence;

}
Loading