Skip to content
1 change: 1 addition & 0 deletions backend/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ dependencies {


// Flyway
implementation 'org.springframework.boot:spring-boot-flyway'
implementation 'org.flywaydb:flyway-core'
implementation 'org.flywaydb:flyway-database-postgresql'

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.example.Piroin.project.domain.assignment.entity;

import com.example.Piroin.project.domain.curriculum.entity.StudySession;
import jakarta.persistence.*;
import lombok.*;
import java.time.LocalDate;

@Entity
@Table(name = "assignment")
Expand All @@ -14,15 +14,15 @@ public class Assignment {

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

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "session_id")
private StudySession session;
private Integer id; // SERIAL 타입에 매칭

@Column(nullable = false)
private String title;

private String content;
}
@Column(length = 255)
private String week;

@Column(name = "session_date")
private LocalDate sessionDate; // DATE 타입에 매칭

}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public class AssignmentItem {

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

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import org.springframework.web.bind.annotation.*;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Optional;

Expand All @@ -33,8 +34,17 @@ public class AdminAttendanceController {
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "잘못된 요청")
})
@PostMapping("/admin/attendance/start")
public AttendanceCodeResponse startAttendance(@PathVariable Long studySessionId) {
AttendanceCode code = attendanceService.generateCodeAndCreateAttendances(studySessionId);
public AttendanceCodeResponse startAttendance() {

// 1. 오늘 날짜 구하기 (LocalDate 활용)
LocalDate today = LocalDate.now();

// 2. 서비스가 원하는 "yyyy-MM-dd" 형식의 문자열로 포맷팅
String todayStr = today.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));

// 3. 포맷팅된 오늘 날짜 문자열을 서비스에 넘겨줍니다.
AttendanceCode code = attendanceService.generateCodeAndCreateAttendances(todayStr);

return AttendanceCodeResponse.from(code);
}

Expand Down Expand Up @@ -71,7 +81,7 @@ public String expireActiveAttendance() {
@PutMapping("/admin/users/{userId}/status")
public boolean updateUserStatus(
@Parameter(description = "사용자 ID", example = "1")
@PathVariable Long userId,
@PathVariable Integer userId,
@RequestBody UpdateUserStatusReq req) {
return attendanceService.updateUserStatus(userId, req);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,11 @@ public ApiResponse<AttendanceMarkResponse> markAttendance(
content = @Content(schema = @Schema(implementation = MarkAttendanceReq.class))
)
@RequestBody MarkAttendanceReq req,
@AuthenticationPrincipal Long userId
@AuthenticationPrincipal Integer userId
) {
// [수정] 서비스 메서드 스펙 변경에 맞춰 req.getStudySessionId()를 제거했습니다.
AttendanceMarkResponse response = attendanceService.markAttendance(
userId,
req.getStudySessionId(),
Long.valueOf(userId),
req.getCode()
);

Expand Down Expand Up @@ -77,7 +77,7 @@ public ApiResponse<AttendanceMarkResponse> markAttendance(
})
@GetMapping("/user")
public ApiResponse<List<AttendanceStatusRes>> getAttendanceByUserId(@AuthenticationPrincipal Long userId) {
return ApiResponse.success(attendanceService.findByUserId(userId));
return ApiResponse.success(attendanceService.findByUserId(Math.toIntExact(userId)));
}

// 3. 특정 유저의 특정 일자 출석 정보
Expand All @@ -93,7 +93,7 @@ public ApiResponse<List<AttendanceSlotRes>> getAttendanceByUserIdAndDate(
@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate date,
@AuthenticationPrincipal Long userId
) {
return ApiResponse.success(attendanceService.findByUserIdAndDate(userId, date));
return ApiResponse.success(attendanceService.findByUserIdAndDate(Math.toIntExact(userId), date));
}


Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
package com.example.Piroin.project.domain.attendance.dto;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@Schema(description = "출석 차시별 상태")
public class AttendanceSlotRes {
private Long studySessionId;

@Schema(description = "출석 코드 ID")
private Integer attendanceCodeId; // 변수명을 의미에 맞게 변경!

private Boolean status;

public AttendanceSlotRes(Long studySessionId, Boolean status) {
this.studySessionId = studySessionId;
// 생성자 파라미터와 주입부도 변경
public AttendanceSlotRes(Integer attendanceCodeId, Boolean status) {
this.attendanceCodeId = attendanceCodeId;
this.status = status;
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
@NoArgsConstructor
@Schema(description = "출석 체크 요청")
public class MarkAttendanceReq {
@Schema(description = "스터디 세션 ID", example = "1")
private Long studySessionId;
// @Schema(description = "스터디 세션 ID", example = "1")
// private Long studySessionId;

@Schema(description = "출석 코드", example = "1234")
private String code;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,13 @@
package com.example.Piroin.project.domain.attendance.entity;

import com.example.Piroin.project.domain.curriculum.entity.StudySession;
import com.example.Piroin.project.domain.user.entity.User;
import jakarta.persistence.*;
import lombok.*;

import javax.xml.crypto.dsig.Manifest;

@Entity
@Table(
name = "attendance",
uniqueConstraints = {
@UniqueConstraint(
name = "uq_attendance_user_session",
columnNames = {"user_id", "study_session_id"}
)
}
)
@Table(name = "attendance")
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
Expand All @@ -23,22 +16,22 @@ public class Attendance {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Integer id; // SERIAL 타입에 매칭 (Long -> Integer)

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
private User user;
@JoinColumn(name = "attendance_code_id", nullable = false)
private AttendanceCode attendanceCode; // attendance_code_id 매핑

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "study_session_id")
private StudySession studySession;
@JoinColumn(name = "user_id", nullable = false)
private User user; // user_id 매핑

@Column(nullable = false)
private Boolean status;
private Boolean status; // BOOLEAN 타입에 매칭

public void updateStatus(Boolean status) {
this.status = status;
}

}

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.example.Piroin.project.domain.attendance.entity;

import com.example.Piroin.project.domain.curriculum.entity.StudySession;
import jakarta.persistence.*;
import lombok.*;

Expand All @@ -14,20 +13,24 @@ public class AttendanceCode {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Integer id; // SERIAL 타입에 매칭 (Long -> Integer)

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "study_session_id")
private StudySession studySession;
@Column(name = "attendance_date")
private String attendanceDate;

@Column(name = "attendance_order")
private String attendanceOrder; // '1, 2, 3' 코멘트 항목

@Column(nullable = false, length = 20)
private String code;

@Column(name = "is_expired", nullable = false)
private Boolean isExpired;
private Boolean isExpired; // BOOLEAN 타입에 매칭

@Column(name = "field3")
private String field3;

public void expire() {
this.isExpired = true;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,29 +14,22 @@

public interface AttendanceCodeRepository extends JpaRepository<AttendanceCode, Long> {

@Query("""
select count(ac)
from AttendanceCode ac
where ac.studySession.sessionDate = :date
""")
int countByStudySessionDate(@Param("date") LocalDate date);

List<AttendanceCode> findByIsExpiredFalse();


// [추가] 모든 활성화된 코드를 한 번에 만료 처리 (벌크 연산)
@Modifying
@Query("update AttendanceCode ac set ac.isExpired = true where ac.isExpired = false")
void expireAllActiveCodes();

Optional<AttendanceCode> findFirstByIsExpiredFalseOrderByIdDesc();

Optional<AttendanceCode> findByCodeAndIsExpiredFalse(String code);
// Optional<AttendanceCode> findByCodeAndStudySessionId(String code, Long studySessionId);

List<AttendanceCode> findByStudySessionId(Long studySessionId);
// 특정 날짜에 발급된 코드 개수 조회
long countByAttendanceDate(String attendanceDate);

Optional<AttendanceCode> findByCodeAndStudySessionId(String code, Long studySessionId);
// 만료되지 않은 코드 목록 조회
List<AttendanceCode> findByIsExpiredFalse();

Optional<AttendanceCode> findByCode(String code);
}


Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package com.example.Piroin.project.domain.attendance.repository;

import com.example.Piroin.project.domain.attendance.entity.AttendanceCode;
import com.example.Piroin.project.domain.user.entity.User;
import com.example.Piroin.project.domain.attendance.entity.Attendance;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import java.time.LocalDate;
Expand All @@ -11,19 +14,33 @@

public interface AttendanceRepository extends JpaRepository<Attendance, Long> {

List<Attendance> findByUserId(Long userId);

Optional<Attendance> findByUserIdAndStudySessionId(Long userId, Long studySessionId);
// List<Attendance> findByUserId(Long userId);

boolean existsByUserIdAndStudySessionId(Long userId, Long studySessionId);
//Optional<Attendance> findByUserIdAndStudySessionId(Integer userId, Long studySessionId);

List<Attendance> findByStudySessionId(Long studySessionId);

List<Attendance> findByStudySessionIdAndStatusFalse(Long studySessionId);
// 연관관계 필드명이 attendanceCode 라면 내부 ID인 Id를 조합하여 명명
Optional<Attendance> findByUserIdAndAttendanceCodeId(Long userId, Long attendanceCodeId);

List<Attendance> findByUserIdAndStudySessionSessionDate(Long userId, LocalDate date);
//List<Attendance> findByUserIdAndStudySessionSessionDate(Integer userId, LocalDate date);

int countByUserAndStatusFalse(User user);

// 1. 특정 출석 코드 ID에 해당하는 결석 데이터 조회
List<Attendance> findByAttendanceCodeIdAndStatusFalse(Integer attendanceCodeId);

// 2. 특정 유저 ID와 출석 코드의 날짜 조건으로 조회 (엔티티 그래프 참조: attendanceCode.attendanceDate)
@Query("SELECT a FROM Attendance a WHERE a.user.id = :userId AND a.attendanceCode.attendanceDate = :attendanceDate")
List<Attendance> findByUserIdAndDate(@Param("userId") Integer userId, @Param("attendanceDate") String attendanceDate);

// 3. 특정 유저의 모든 출석 데이터 조회
List<Attendance> findByUserId(Long userId);

// 특정 날짜에 발급된 출석 코드의 개수를 세는 메서드
//long countByAttendanceDate(String attendanceDate);

// 현재 만료되지 않은(활성화된) 출석 코드 목록을 가져오는 메서드
//List<AttendanceCode> findByIsExpiredFalse();
}


Loading
Loading