From 24108c98b4d2c1bca463ab0fdd45c62e024f2b3b Mon Sep 17 00:00:00 2001 From: lilyyang0077 Date: Sat, 16 May 2026 21:11:37 +0900 Subject: [PATCH 01/12] =?UTF-8?q?feat:=20flyway=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/build.gradle | 1 + .../V2__add_recording_password_to_study_session.sql | 2 -- ...__add_material_and_assignment_name_to_study_session.sql | 5 ----- .../V4__add_parent_comment_to_question_comment.sql | 7 ------- 4 files changed, 1 insertion(+), 14 deletions(-) delete mode 100644 backend/src/main/resources/db/migration/V2__add_recording_password_to_study_session.sql delete mode 100644 backend/src/main/resources/db/migration/V3__add_material_and_assignment_name_to_study_session.sql delete mode 100644 backend/src/main/resources/db/migration/V4__add_parent_comment_to_question_comment.sql diff --git a/backend/build.gradle b/backend/build.gradle index 575981e..7aa086d 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -34,6 +34,7 @@ dependencies { // Flyway + implementation 'org.springframework.boot:spring-boot-flyway' implementation 'org.flywaydb:flyway-core' implementation 'org.flywaydb:flyway-database-postgresql' diff --git a/backend/src/main/resources/db/migration/V2__add_recording_password_to_study_session.sql b/backend/src/main/resources/db/migration/V2__add_recording_password_to_study_session.sql deleted file mode 100644 index 6529279..0000000 --- a/backend/src/main/resources/db/migration/V2__add_recording_password_to_study_session.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE study_session - ADD COLUMN recording_password VARCHAR(60); \ No newline at end of file diff --git a/backend/src/main/resources/db/migration/V3__add_material_and_assignment_name_to_study_session.sql b/backend/src/main/resources/db/migration/V3__add_material_and_assignment_name_to_study_session.sql deleted file mode 100644 index b952e79..0000000 --- a/backend/src/main/resources/db/migration/V3__add_material_and_assignment_name_to_study_session.sql +++ /dev/null @@ -1,5 +0,0 @@ -ALTER TABLE study_session - ADD COLUMN session_material_name VARCHAR(255); - -ALTER TABLE study_session - ADD COLUMN assignment_name VARCHAR(255); \ No newline at end of file diff --git a/backend/src/main/resources/db/migration/V4__add_parent_comment_to_question_comment.sql b/backend/src/main/resources/db/migration/V4__add_parent_comment_to_question_comment.sql deleted file mode 100644 index 02b4c05..0000000 --- a/backend/src/main/resources/db/migration/V4__add_parent_comment_to_question_comment.sql +++ /dev/null @@ -1,7 +0,0 @@ --- question_comment 테이블에 대댓글을 위한 parent_comment_id 컬럼 추가 --- Issue #4에서 QuestionComment 엔티티에 parentComment 필드가 추가되었는데 DB에 반영되지 않아 이 마이그레이션으로 동기화 - -ALTER TABLE question_comment - ADD COLUMN parent_comment_id BIGINT, - ADD CONSTRAINT fk_question_comment_parent - FOREIGN KEY (parent_comment_id) REFERENCES question_comment (id); \ No newline at end of file From 8c5f502290fa2eb68669cb191cfd30762125455a Mon Sep 17 00:00:00 2001 From: lilyyang0077 Date: Sat, 16 May 2026 21:12:25 +0900 Subject: [PATCH 02/12] =?UTF-8?q?refactor:=20=EC=88=98=EC=A0=95=EB=90=9C?= =?UTF-8?q?=20ERD=EC=97=90=20=EB=A7=9E=EA=B2=8C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/resources/db/migration/V1__init.sql | 308 ++++++++---------- 1 file changed, 127 insertions(+), 181 deletions(-) diff --git a/backend/src/main/resources/db/migration/V1__init.sql b/backend/src/main/resources/db/migration/V1__init.sql index c5d41bb..7056c81 100644 --- a/backend/src/main/resources/db/migration/V1__init.sql +++ b/backend/src/main/resources/db/migration/V1__init.sql @@ -1,210 +1,156 @@ -CREATE TYPE role_enum AS ENUM ('ADMIN', 'MEMBER'); - -CREATE TYPE session_day_part_enum AS ENUM ('AM', 'PM'); - -CREATE TYPE session_status_enum AS ENUM ( - 'BEFORE_SESSION', - 'IN_SESSION', - 'AFTER_SESSION' -); - -CREATE TYPE assignment_status_enum AS ENUM ( - 'SUCCESS', - 'INSUFFICIENT', - 'FAILURE' +-- 1. ENUM 타입을 대체할 임시 타입 정의 (혹은 테이블 생성시 CHECK 제약조건 사용 가능) +CREATE TYPE submission_status AS ENUM ('SUCCESS', 'INSUFFICIENT', 'FAILURE'); +CREATE TYPE session_status AS ENUM ('BEFORE_SESSION', 'IN_SESSION', 'AFTER_SESSION'); +CREATE TYPE day_part_type AS ENUM ('AM', 'PM'); +CREATE TYPE choice_type AS ENUM ('UNDERSTOOD', 'NOT_UNDERSTOOD'); +CREATE TYPE role_type AS ENUM ('ADMIN', 'MEMBER'); + +-- 2. 테이블 생성 (백틱 제거, TINYINT -> SMALLINT/BOOLEAN 변경) +CREATE TABLE assignment ( + id SERIAL NOT NULL, + date_id INT NOT NULL, -- SERIAL은 PK용이므로 FK가 될 곳은 INT로 변경 + title VARCHAR(255) NOT NULL, + content VARCHAR(255) NULL ); -CREATE TYPE understanding_choice_enum AS ENUM ( - 'UNDERSTOOD', - 'NOT_UNDERSTOOD' +CREATE TABLE understanding_response ( + id BIGINT NOT NULL, + check_id BIGINT NOT NULL, + user_id BIGINT NOT NULL, + choice choice_type NOT NULL, + created_at TIMESTAMP NOT NULL ); CREATE TABLE users ( - id BIGINT NOT NULL, - password VARCHAR(255) NOT NULL, - name VARCHAR(100) NOT NULL, - email VARCHAR(255), - phone VARCHAR(50), - role role_enum NOT NULL DEFAULT 'MEMBER', - generation INT, - CONSTRAINT pk_users PRIMARY KEY (id) + id SERIAL NOT NULL, + password VARCHAR(100) NOT NULL, + name VARCHAR(100) NOT NULL, + email VARCHAR(255) NULL, + phone VARCHAR(50) NULL, + role role_type NOT NULL DEFAULT 'MEMBER', + generation INT NULL ); -CREATE TABLE study_session ( - id BIGINT NOT NULL, - created_by BIGINT NOT NULL, - generation INT NOT NULL, - week BIGINT NOT NULL, - session_date DATE NOT NULL, - day_part session_day_part_enum NOT NULL, - title VARCHAR(255) NOT NULL, - host_name VARCHAR(100), - status session_status_enum NOT NULL DEFAULT 'BEFORE_SESSION', - description TEXT, - session_material_url TEXT, - assignment_url TEXT, - recording_url TEXT, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - CONSTRAINT pk_study_session PRIMARY KEY (id), - CONSTRAINT fk_study_session_created_by - FOREIGN KEY (created_by) REFERENCES users (id) +CREATE TABLE assignment_item ( + id SERIAL NOT NULL, + user_id INT NOT NULL, + assignment_id INT NOT NULL, + submitted submission_status NOT NULL DEFAULT 'SUCCESS' ); CREATE TABLE attendance_code ( - id BIGINT NOT NULL, - study_session_id BIGINT, - code VARCHAR(20) NOT NULL, - is_expired BOOLEAN NOT NULL, - CONSTRAINT pk_attendance_code PRIMARY KEY (id), - CONSTRAINT fk_attendance_code_session - FOREIGN KEY (study_session_id) REFERENCES study_session (id) + id SERIAL NOT NULL, + date_id INT NOT NULL, + attendance_order SMALLINT NULL, -- TINYINT를 SMALLINT로 변경 + code VARCHAR(20) NOT NULL, + is_expired BOOLEAN NOT NULL, -- TINYINT(1)을 BOOLEAN으로 변경 + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); -CREATE TABLE attendance ( - id BIGINT NOT NULL, - user_id BIGINT NOT NULL, - study_session_id BIGINT, - status BOOLEAN NOT NULL, - CONSTRAINT pk_attendance PRIMARY KEY (id), - CONSTRAINT uq_attendance_user_session - UNIQUE (user_id, study_session_id), - CONSTRAINT fk_attendance_user - FOREIGN KEY (user_id) REFERENCES users (id), - CONSTRAINT fk_attendance_session - FOREIGN KEY (study_session_id) REFERENCES study_session (id) +CREATE TABLE question_like ( + id BIGINT NOT NULL, + question_id BIGINT NOT NULL, + user_id BIGINT NOT NULL, + created_at TIMESTAMP NOT NULL ); -CREATE TABLE deposit ( - id BIGINT NOT NULL, - user_id BIGINT NOT NULL, - amount INT NOT NULL, - descent_assignment INT NOT NULL, - descent_attendance INT NOT NULL, - ascent_defence INT NOT NULL, - CONSTRAINT pk_deposit PRIMARY KEY (id), - CONSTRAINT uq_deposit_user - UNIQUE (user_id), - CONSTRAINT fk_deposit_user - FOREIGN KEY (user_id) REFERENCES users (id) +CREATE TABLE question_anonymous_identity ( + id SERIAL NOT NULL, + user_id INT NOT NULL, + question_id INT NOT NULL, + anonymous_no INT NOT NULL DEFAULT 1, + created_at TIMESTAMP NOT NULL ); -CREATE TABLE assignment ( - id BIGINT NOT NULL, - session_id BIGINT, - title VARCHAR(255) NOT NULL, - content VARCHAR(255), - CONSTRAINT pk_assignment PRIMARY KEY (id), - CONSTRAINT fk_assignment_session - FOREIGN KEY (session_id) REFERENCES study_session (id) +CREATE TABLE deposit ( + id SERIAL NOT NULL, + user_id INT NOT NULL, + amount INT NOT NULL, + descent_assignment INT NOT NULL, + descent_attendance INT NOT NULL, + ascent_defence INT NOT NULL ); -CREATE TABLE assignment_item ( - id BIGINT NOT NULL, - user_id BIGINT NOT NULL, - assignment_id BIGINT NOT NULL, - submitted assignment_status_enum NOT NULL DEFAULT 'SUCCESS', - CONSTRAINT pk_assignment_item PRIMARY KEY (id), - CONSTRAINT uq_assignment_item_user_assignment - UNIQUE (user_id, assignment_id), - CONSTRAINT fk_assignment_item_user - FOREIGN KEY (user_id) REFERENCES users (id), - CONSTRAINT fk_assignment_item_assignment - FOREIGN KEY (assignment_id) REFERENCES assignment (id) +CREATE TABLE understanding_check ( + id BIGINT NOT NULL, + session_id BIGINT NOT NULL, + created_by BIGINT NOT NULL, + title VARCHAR(255) NOT NULL, + description VARCHAR(255) NULL, + created_at TIMESTAMP NOT NULL, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); -CREATE TABLE question ( - id BIGINT NOT NULL, - session_id BIGINT NOT NULL, - user_id BIGINT NOT NULL, - content TEXT NOT NULL, - image_url TEXT, - is_resolved BOOLEAN NOT NULL, - like_count INT NOT NULL, - created_at TIMESTAMP NOT NULL, - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - deleted_at TIMESTAMP, - CONSTRAINT pk_question PRIMARY KEY (id), - CONSTRAINT fk_question_session - FOREIGN KEY (session_id) REFERENCES study_session (id), - CONSTRAINT fk_question_user - FOREIGN KEY (user_id) REFERENCES users (id) +CREATE TABLE date ( + id SERIAL NOT NULL, + date date NULL ); CREATE TABLE question_comment ( - id BIGINT NOT NULL, - question_id BIGINT NOT NULL, - user_id BIGINT NOT NULL, - content TEXT NOT NULL, - image_url TEXT, - created_at TIMESTAMP NOT NULL, - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - deleted_at TIMESTAMP, - CONSTRAINT pk_question_comment PRIMARY KEY (id), - CONSTRAINT fk_question_comment_question - FOREIGN KEY (question_id) REFERENCES question (id), - CONSTRAINT fk_question_comment_user - FOREIGN KEY (user_id) REFERENCES users (id) + id SERIAL NOT NULL, + question_id INT NOT NULL, + user_id INT NOT NULL, + parent_comment_id INT NULL, + content VARCHAR(1000) NOT NULL, + image_url VARCHAR(1000) NULL, + created_at TIMESTAMP NOT NULL, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted_at TIMESTAMP NULL ); -CREATE TABLE question_like ( - id BIGINT NOT NULL, - question_id BIGINT NOT NULL, - user_id BIGINT NOT NULL, - created_at TIMESTAMP NOT NULL, - CONSTRAINT pk_question_like PRIMARY KEY (id), - CONSTRAINT uq_question_like_question_user - UNIQUE (question_id, user_id), - CONSTRAINT fk_question_like_question - FOREIGN KEY (question_id) REFERENCES question (id), - CONSTRAINT fk_question_like_user - FOREIGN KEY (user_id) REFERENCES users (id) -); - -CREATE TABLE question_anonymous_identity ( - id BIGINT NOT NULL, - user_id BIGINT NOT NULL, - question_id BIGINT NOT NULL, - anonymous_no INT NOT NULL DEFAULT 1, - created_at TIMESTAMP NOT NULL, - CONSTRAINT pk_question_anonymous_identity PRIMARY KEY (id), - CONSTRAINT uq_question_anon_question_user - UNIQUE (question_id, user_id), - CONSTRAINT uq_question_anon_question_no - UNIQUE (question_id, anonymous_no), - CONSTRAINT fk_question_anon_user - FOREIGN KEY (user_id) REFERENCES users (id), - CONSTRAINT fk_question_anon_question - FOREIGN KEY (question_id) REFERENCES question (id) +CREATE TABLE study_session ( + id SERIAL NOT NULL, + date_id INT NOT NULL, + created_by INT NOT NULL, + generation INT NULL, + week INT NOT NULL, + day_part day_part_type NOT NULL, + title VARCHAR(255) NOT NULL, + host_name VARCHAR(100) NOT NULL, + description VARCHAR(1000) NULL, + session_material_name VARCHAR(255) NULL, + status session_status NOT NULL DEFAULT 'BEFORE_SESSION', + session_material_url VARCHAR(1000) NULL, + assignment_name VARCHAR(255) NULL, + assignment_url VARCHAR(1000) NULL, + recording_url VARCHAR(1000) NULL, + recording_password VARCHAR(60) NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); -CREATE TABLE understanding_check ( - id BIGINT NOT NULL, - session_id BIGINT NOT NULL, - created_by BIGINT NOT NULL, - title VARCHAR(255) NOT NULL, - description VARCHAR(255), - created_at TIMESTAMP NOT NULL, - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - CONSTRAINT pk_understanding_check PRIMARY KEY (id), - CONSTRAINT fk_understanding_check_session - FOREIGN KEY (session_id) REFERENCES study_session (id), - CONSTRAINT fk_understanding_check_created_by - FOREIGN KEY (created_by) REFERENCES users (id) +CREATE TABLE attendance ( + id SERIAL NOT NULL, + attendance_code_id INT NOT NULL, + user_id INT NOT NULL, + status BOOLEAN NOT NULL ); -CREATE TABLE understanding_response ( - id BIGINT NOT NULL, - check_id BIGINT NOT NULL, - user_id BIGINT NOT NULL, - choice understanding_choice_enum NOT NULL, - created_at TIMESTAMP NOT NULL, - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - CONSTRAINT pk_understanding_response PRIMARY KEY (id), - CONSTRAINT uq_understanding_response_check_user - UNIQUE (check_id, user_id), - CONSTRAINT fk_understanding_response_check - FOREIGN KEY (check_id) REFERENCES understanding_check (id), - CONSTRAINT fk_understanding_response_user - FOREIGN KEY (user_id) REFERENCES users (id) -); +CREATE TABLE question ( + id SERIAL NOT NULL, + session_id INT NOT NULL, + user_id INT NOT NULL, + content VARCHAR(1000) NOT NULL, + image_url VARCHAR(1000) NULL, + is_resolved BOOLEAN NOT NULL, + like_count INT NOT NULL, + created_at TIMESTAMP NOT NULL, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted_at TIMESTAMP NULL +); + +-- 제약 조건 추가 +ALTER TABLE assignment ADD CONSTRAINT PK_ASSIGNMENT PRIMARY KEY (id); +ALTER TABLE understanding_response ADD CONSTRAINT PK_UNDERSTANDING_RESPONSE PRIMARY KEY (id); +ALTER TABLE users ADD CONSTRAINT PK_USERS PRIMARY KEY (id); +ALTER TABLE assignment_item ADD CONSTRAINT PK_ASSIGNMENT_ITEM PRIMARY KEY (id); +ALTER TABLE attendance_code ADD CONSTRAINT PK_ATTENDANCE_CODE PRIMARY KEY (id); +ALTER TABLE question_like ADD CONSTRAINT PK_QUESTION_LIKE PRIMARY KEY (id); +ALTER TABLE question_anonymous_identity ADD CONSTRAINT PK_QUESTION_ANONYMOUS_IDENTITY PRIMARY KEY (id); +ALTER TABLE deposit ADD CONSTRAINT PK_DEPOSIT PRIMARY KEY (id); +ALTER TABLE understanding_check ADD CONSTRAINT PK_UNDERSTANDING_CHECK PRIMARY KEY (id); +ALTER TABLE date ADD CONSTRAINT PK_DATE PRIMARY KEY (id); +ALTER TABLE question_comment ADD CONSTRAINT PK_QUESTION_COMMENT PRIMARY KEY (id); +ALTER TABLE study_session ADD CONSTRAINT PK_STUDY_SESSION PRIMARY KEY (id); +ALTER TABLE attendance ADD CONSTRAINT PK_ATTENDANCE PRIMARY KEY (id); +ALTER TABLE question ADD CONSTRAINT PK_QUESTION PRIMARY KEY (id); \ No newline at end of file From 56e7825e2a23ee87ec3d526cec0be9b07b5835a7 Mon Sep 17 00:00:00 2001 From: lilyyang0077 Date: Sat, 16 May 2026 21:12:54 +0900 Subject: [PATCH 03/12] =?UTF-8?q?chore:=20DB=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EB=B0=94=EA=BF=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/main/resources/application.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/backend/src/main/resources/application.yml b/backend/src/main/resources/application.yml index 713cb76..d094235 100644 --- a/backend/src/main/resources/application.yml +++ b/backend/src/main/resources/application.yml @@ -4,19 +4,19 @@ jwt: spring: datasource: - url: ${DB_URL} - username: ${DB_USER} - password: ${DB_PASSWORD} + url: jdbc:postgresql://${RDS_ENDPOINT}:5432/${RDS_DB_NAME} + username: ${RDS_USERNAME} + password: ${RDS_PASSWORD} driver-class-name: org.postgresql.Driver + flyway: + enabled: true + jpa: hibernate: - # create: 실행할 때마다 테이블을 새로 만듦 (연습용) - # update: 변경된 부분만 수정해서 반영 (개발용 권장) - ddl-auto: update - show-sql: true # 콘솔에 SQL 문이 찍히게 설정 + ddl-auto: validate + show-sql: true properties: hibernate: format_sql: true - dialect: org.hibernate.dialect.PostgreSQLDialect packagesToScan: com.example.Piroin.project.domain From d97297b37be7246fcb8c441992a145be3531191c Mon Sep 17 00:00:00 2001 From: lilyyang0077 Date: Sun, 17 May 2026 14:32:53 +0900 Subject: [PATCH 04/12] =?UTF-8?q?fix:=20=EC=B5=9C=EC=A2=85=20ERD=EC=97=90?= =?UTF-8?q?=20=EB=A7=9E=EA=B2=8C=20flyway=20script=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/resources/db/migration/V1__init.sql | 220 +++++++++--------- 1 file changed, 107 insertions(+), 113 deletions(-) diff --git a/backend/src/main/resources/db/migration/V1__init.sql b/backend/src/main/resources/db/migration/V1__init.sql index 7056c81..e35b28e 100644 --- a/backend/src/main/resources/db/migration/V1__init.sql +++ b/backend/src/main/resources/db/migration/V1__init.sql @@ -1,25 +1,4 @@ --- 1. ENUM 타입을 대체할 임시 타입 정의 (혹은 테이블 생성시 CHECK 제약조건 사용 가능) -CREATE TYPE submission_status AS ENUM ('SUCCESS', 'INSUFFICIENT', 'FAILURE'); -CREATE TYPE session_status AS ENUM ('BEFORE_SESSION', 'IN_SESSION', 'AFTER_SESSION'); -CREATE TYPE day_part_type AS ENUM ('AM', 'PM'); -CREATE TYPE choice_type AS ENUM ('UNDERSTOOD', 'NOT_UNDERSTOOD'); -CREATE TYPE role_type AS ENUM ('ADMIN', 'MEMBER'); - --- 2. 테이블 생성 (백틱 제거, TINYINT -> SMALLINT/BOOLEAN 변경) -CREATE TABLE assignment ( - id SERIAL NOT NULL, - date_id INT NOT NULL, -- SERIAL은 PK용이므로 FK가 될 곳은 INT로 변경 - title VARCHAR(255) NOT NULL, - content VARCHAR(255) NULL -); - -CREATE TABLE understanding_response ( - id BIGINT NOT NULL, - check_id BIGINT NOT NULL, - user_id BIGINT NOT NULL, - choice choice_type NOT NULL, - created_at TIMESTAMP NOT NULL -); +-- 1. 테이블 생성 (기본키 포함) CREATE TABLE users ( id SERIAL NOT NULL, @@ -27,103 +6,69 @@ CREATE TABLE users ( name VARCHAR(100) NOT NULL, email VARCHAR(255) NULL, phone VARCHAR(50) NULL, - role role_type NOT NULL DEFAULT 'MEMBER', - generation INT NULL -); - -CREATE TABLE assignment_item ( - id SERIAL NOT NULL, - user_id INT NOT NULL, - assignment_id INT NOT NULL, - submitted submission_status NOT NULL DEFAULT 'SUCCESS' -); - -CREATE TABLE attendance_code ( - id SERIAL NOT NULL, - date_id INT NOT NULL, - attendance_order SMALLINT NULL, -- TINYINT를 SMALLINT로 변경 - code VARCHAR(20) NOT NULL, - is_expired BOOLEAN NOT NULL, -- TINYINT(1)을 BOOLEAN으로 변경 - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP -); - -CREATE TABLE question_like ( - id BIGINT NOT NULL, - question_id BIGINT NOT NULL, - user_id BIGINT NOT NULL, - created_at TIMESTAMP NOT NULL -); - -CREATE TABLE question_anonymous_identity ( - id SERIAL NOT NULL, - user_id INT NOT NULL, - question_id INT NOT NULL, - anonymous_no INT NOT NULL DEFAULT 1, - created_at TIMESTAMP NOT NULL -); - -CREATE TABLE deposit ( - id SERIAL NOT NULL, - user_id INT NOT NULL, - amount INT NOT NULL, - descent_assignment INT NOT NULL, - descent_attendance INT NOT NULL, - ascent_defence INT NOT NULL -); - -CREATE TABLE understanding_check ( - id BIGINT NOT NULL, - session_id BIGINT NOT NULL, - created_by BIGINT NOT NULL, - title VARCHAR(255) NOT NULL, - description VARCHAR(255) NULL, - created_at TIMESTAMP NOT NULL, - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP -); - -CREATE TABLE date ( - id SERIAL NOT NULL, - date date NULL -); - -CREATE TABLE question_comment ( - id SERIAL NOT NULL, - question_id INT NOT NULL, - user_id INT NOT NULL, - parent_comment_id INT NULL, - content VARCHAR(1000) NOT NULL, - image_url VARCHAR(1000) NULL, - created_at TIMESTAMP NOT NULL, - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - deleted_at TIMESTAMP NULL + role VARCHAR(20) NOT NULL DEFAULT 'MEMBER', -- ENUM 대신 VARCHAR + CHECK 제약 조건 활용 + generation INT NULL, + CONSTRAINT PK_USERS PRIMARY KEY (id), + CONSTRAINT CHK_USERS_ROLE CHECK (role IN ('ADMIN', 'MEMBER')) ); CREATE TABLE study_session ( id SERIAL NOT NULL, - date_id INT NOT NULL, created_by INT NOT NULL, generation INT NULL, week INT NOT NULL, - day_part day_part_type NOT NULL, + session_date DATE NOT NULL, + day_part VARCHAR(10) NOT NULL, title VARCHAR(255) NOT NULL, host_name VARCHAR(100) NOT NULL, + status VARCHAR(30) NOT NULL DEFAULT 'BEFORE_SESSION', description VARCHAR(1000) NULL, session_material_name VARCHAR(255) NULL, - status session_status NOT NULL DEFAULT 'BEFORE_SESSION', session_material_url VARCHAR(1000) NULL, assignment_name VARCHAR(255) NULL, assignment_url VARCHAR(1000) NULL, recording_url VARCHAR(1000) NULL, recording_password VARCHAR(60) NULL, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT PK_STUDY_SESSION PRIMARY KEY (id), + CONSTRAINT CHK_STUDY_SESSION_DAY_PART CHECK (day_part IN ('AM', 'PM')), + CONSTRAINT CHK_STUDY_SESSION_STATUS CHECK (status IN ('BEFORE_SESSION', 'IN_SESSION', 'AFTER_SESSION')) +); + +CREATE TABLE assignment ( + id SERIAL NOT NULL, + title VARCHAR(255) NOT NULL, + week VARCHAR(255) NULL, + session_date DATE NULL, + CONSTRAINT PK_ASSIGNMENT PRIMARY KEY (id) +); + +CREATE TABLE assignment_item ( + id SERIAL NOT NULL, + user_id INT NOT NULL, -- FK 대상이므로 SERIAL에서 INT로 수정 + assignment_id INT NOT NULL, -- FK 대상이므로 SERIAL에서 INT로 수정 + submitted VARCHAR(20) NOT NULL DEFAULT 'SUCCESS', + CONSTRAINT PK_ASSIGNMENT_ITEM PRIMARY KEY (id), + CONSTRAINT CHK_ASSIGNMENT_ITEM_SUBMITTED CHECK (submitted IN ('SUCCESS', 'INSUFFICIENT', 'FAILURE')) +); + +CREATE TABLE attendance_code ( + id SERIAL NOT NULL, + attendance_date VARCHAR(255) NULL, + attendance_order VARCHAR(255) NULL, + code VARCHAR(20) NOT NULL, + is_expired BOOLEAN NOT NULL, -- TINYINT(1)에서 BOOLEAN으로 수정 + Field3 VARCHAR(255) NULL, + CONSTRAINT PK_ATTENDANCE_CODE PRIMARY KEY (id) ); CREATE TABLE attendance ( id SERIAL NOT NULL, attendance_code_id INT NOT NULL, user_id INT NOT NULL, - status BOOLEAN NOT NULL + status BOOLEAN NOT NULL, -- TINYINT(1)에서 BOOLEAN으로 수정 + CONSTRAINT PK_ATTENDANCE PRIMARY KEY (id) ); CREATE TABLE question ( @@ -132,25 +77,74 @@ CREATE TABLE question ( user_id INT NOT NULL, content VARCHAR(1000) NOT NULL, image_url VARCHAR(1000) NULL, - is_resolved BOOLEAN NOT NULL, + is_resolved BOOLEAN NOT NULL, -- TINYINT(1)에서 BOOLEAN으로 수정 like_count INT NOT NULL, created_at TIMESTAMP NOT NULL, updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - deleted_at TIMESTAMP NULL + deleted_at TIMESTAMP NULL, + CONSTRAINT PK_QUESTION PRIMARY KEY (id) +); + +CREATE TABLE question_comment ( + id SERIAL NOT NULL, + question_id INT NOT NULL, + user_id INT NOT NULL, + parent_comment_id INT NULL, + content VARCHAR(1000) NOT NULL, + image_url VARCHAR(1000) NULL, + created_at TIMESTAMP NOT NULL, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted_at TIMESTAMP NULL, + CONSTRAINT PK_QUESTION_COMMENT PRIMARY KEY (id) +); + +CREATE TABLE question_anonymous_identity ( + id SERIAL NOT NULL, + user_id INT NOT NULL, + question_id INT NOT NULL, + anonymous_no INT NOT NULL DEFAULT 1, + created_at TIMESTAMP NOT NULL, + CONSTRAINT PK_QUESTION_ANONYMOUS_IDENTITY PRIMARY KEY (id) +); + +CREATE TABLE question_like ( + id BIGSERIAL NOT NULL, -- PK 타입 매칭을 위해 BIGSERIAL 수정 + question_id BIGINT NOT NULL, + user_id BIGINT NOT NULL, + created_at TIMESTAMP NOT NULL, + CONSTRAINT PK_QUESTION_LIKE PRIMARY KEY (id) +); + +CREATE TABLE understanding_check ( + id BIGSERIAL NOT NULL, -- BIGINT PK용 BIGSERIAL 수정 + session_id BIGINT NOT NULL, + created_by BIGINT NOT NULL, + title VARCHAR(255) NOT NULL, + description VARCHAR(255) NULL, + created_at TIMESTAMP NOT NULL, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT PK_UNDERSTANDING_CHECK PRIMARY KEY (id) +); + +CREATE TABLE understanding_response ( + id BIGSERIAL NOT NULL, + check_id BIGINT NOT NULL, + user_id BIGINT NOT NULL, + choice VARCHAR(20) NOT NULL, + created_at TIMESTAMP NOT NULL, + CONSTRAINT PK_UNDERSTANDING_RESPONSE PRIMARY KEY (id), + CONSTRAINT CHK_UNDERSTANDING_RESPONSE_CHOICE CHECK (choice IN ('UNDERSTOOD', 'NOT_UNDERSTOOD')) +); + +CREATE TABLE deposit ( + id SERIAL NOT NULL, + user_id INT NOT NULL, + amount INT NOT NULL, + descent_assignment INT NOT NULL, + descent_attendance INT NOT NULL, + ascent_defence INT NOT NULL, + CONSTRAINT PK_DEPOSIT PRIMARY KEY (id) ); --- 제약 조건 추가 -ALTER TABLE assignment ADD CONSTRAINT PK_ASSIGNMENT PRIMARY KEY (id); -ALTER TABLE understanding_response ADD CONSTRAINT PK_UNDERSTANDING_RESPONSE PRIMARY KEY (id); -ALTER TABLE users ADD CONSTRAINT PK_USERS PRIMARY KEY (id); -ALTER TABLE assignment_item ADD CONSTRAINT PK_ASSIGNMENT_ITEM PRIMARY KEY (id); -ALTER TABLE attendance_code ADD CONSTRAINT PK_ATTENDANCE_CODE PRIMARY KEY (id); -ALTER TABLE question_like ADD CONSTRAINT PK_QUESTION_LIKE PRIMARY KEY (id); -ALTER TABLE question_anonymous_identity ADD CONSTRAINT PK_QUESTION_ANONYMOUS_IDENTITY PRIMARY KEY (id); -ALTER TABLE deposit ADD CONSTRAINT PK_DEPOSIT PRIMARY KEY (id); -ALTER TABLE understanding_check ADD CONSTRAINT PK_UNDERSTANDING_CHECK PRIMARY KEY (id); -ALTER TABLE date ADD CONSTRAINT PK_DATE PRIMARY KEY (id); -ALTER TABLE question_comment ADD CONSTRAINT PK_QUESTION_COMMENT PRIMARY KEY (id); -ALTER TABLE study_session ADD CONSTRAINT PK_STUDY_SESSION PRIMARY KEY (id); -ALTER TABLE attendance ADD CONSTRAINT PK_ATTENDANCE PRIMARY KEY (id); -ALTER TABLE question ADD CONSTRAINT PK_QUESTION PRIMARY KEY (id); \ No newline at end of file +-- 2. 코멘트(주석) 설정 +COMMENT ON COLUMN attendance_code.attendance_order IS '1, 2, 3'; \ No newline at end of file From c08398bf67441c37ff36f8bba8104c00a6c36915 Mon Sep 17 00:00:00 2001 From: lilyyang0077 Date: Sun, 17 May 2026 14:42:02 +0900 Subject: [PATCH 05/12] =?UTF-8?q?fix:=20=EC=88=98=EC=A0=95=EB=90=9C=20ERD?= =?UTF-8?q?=EC=97=90=20=EB=A7=9E=EC=B6=B0=20=EC=97=94=ED=8B=B0=ED=8B=B0=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/assignment/entity/Assignment.java | 16 +++++----- .../domain/attendance/entity/Attendance.java | 30 ++++++++----------- .../attendance/entity/AttendanceCode.java | 19 +++++++----- 3 files changed, 31 insertions(+), 34 deletions(-) diff --git a/backend/src/main/java/com/example/Piroin/project/domain/assignment/entity/Assignment.java b/backend/src/main/java/com/example/Piroin/project/domain/assignment/entity/Assignment.java index 089f951..b439938 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/assignment/entity/Assignment.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/assignment/entity/Assignment.java @@ -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") @@ -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 타입에 매칭 +} \ No newline at end of file diff --git a/backend/src/main/java/com/example/Piroin/project/domain/attendance/entity/Attendance.java b/backend/src/main/java/com/example/Piroin/project/domain/attendance/entity/Attendance.java index 2411395..c3ad840 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/attendance/entity/Attendance.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/attendance/entity/Attendance.java @@ -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 @@ -23,22 +16,23 @@ 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; } -} - + public Manifest getStudySession() { + } +} \ No newline at end of file diff --git a/backend/src/main/java/com/example/Piroin/project/domain/attendance/entity/AttendanceCode.java b/backend/src/main/java/com/example/Piroin/project/domain/attendance/entity/AttendanceCode.java index acb4f4a..efe7f82 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/attendance/entity/AttendanceCode.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/attendance/entity/AttendanceCode.java @@ -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.*; @@ -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; } -} - +} \ No newline at end of file From ff43779f618e1a5699e8df85930a5dc18060c295 Mon Sep 17 00:00:00 2001 From: lilyyang0077 Date: Sun, 17 May 2026 15:14:38 +0900 Subject: [PATCH 06/12] =?UTF-8?q?fix:=20Id=20=ED=83=80=EC=9E=85=20?= =?UTF-8?q?=EC=A2=85=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Piroin/project/domain/attendance/entity/Attendance.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/backend/src/main/java/com/example/Piroin/project/domain/attendance/entity/Attendance.java b/backend/src/main/java/com/example/Piroin/project/domain/attendance/entity/Attendance.java index c3ad840..683e7a2 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/attendance/entity/Attendance.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/attendance/entity/Attendance.java @@ -33,6 +33,5 @@ public void updateStatus(Boolean status) { this.status = status; } - public Manifest getStudySession() { - } + } \ No newline at end of file From 8b8b6ef393ab026124e0ede814bcfb0aaa875bc8 Mon Sep 17 00:00:00 2001 From: lilyyang0077 Date: Sun, 17 May 2026 15:15:08 +0900 Subject: [PATCH 07/12] =?UTF-8?q?fix:=20=EC=88=98=EC=A0=95=EB=90=9C=20enti?= =?UTF-8?q?ty=EC=97=90=20=EB=A7=9E=EC=B6=B0=20=EB=B2=84=EA=B7=B8=20?= =?UTF-8?q?=EA=B3=A0=EC=B9=98=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/AdminAttendanceController.java | 2 +- .../controller/AttendanceController.java | 4 +- .../attendance/dto/AttendanceSlotRes.java | 15 ++-- .../repository/AttendanceCodeRepository.java | 9 ++- .../repository/AttendanceRepository.java | 11 ++- .../attendance/service/AttendanceService.java | 75 +++++++++++-------- .../repository/CurriculumRepository.java | 2 +- .../curriculum/service/CurriculumService.java | 4 +- .../question/service/QuestionService.java | 2 +- .../main/resources/db/migration/V1__init.sql | 2 +- 10 files changed, 76 insertions(+), 50 deletions(-) diff --git a/backend/src/main/java/com/example/Piroin/project/domain/attendance/controller/AdminAttendanceController.java b/backend/src/main/java/com/example/Piroin/project/domain/attendance/controller/AdminAttendanceController.java index 11fb13a..aef7c78 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/attendance/controller/AdminAttendanceController.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/attendance/controller/AdminAttendanceController.java @@ -34,7 +34,7 @@ public class AdminAttendanceController { }) @PostMapping("/admin/attendance/start") public AttendanceCodeResponse startAttendance(@PathVariable Long studySessionId) { - AttendanceCode code = attendanceService.generateCodeAndCreateAttendances(studySessionId); + AttendanceCode code = attendanceService.generateCodeAndCreateAttendances(Math.toIntExact(studySessionId)); return AttendanceCodeResponse.from(code); } diff --git a/backend/src/main/java/com/example/Piroin/project/domain/attendance/controller/AttendanceController.java b/backend/src/main/java/com/example/Piroin/project/domain/attendance/controller/AttendanceController.java index 30a4fce..79eb44b 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/attendance/controller/AttendanceController.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/attendance/controller/AttendanceController.java @@ -77,7 +77,7 @@ public ApiResponse markAttendance( }) @GetMapping("/user") public ApiResponse> getAttendanceByUserId(@AuthenticationPrincipal Long userId) { - return ApiResponse.success(attendanceService.findByUserId(userId)); + return ApiResponse.success(attendanceService.findByUserId(Math.toIntExact(userId))); } // 3. 특정 유저의 특정 일자 출석 정보 @@ -93,7 +93,7 @@ public ApiResponse> 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)); } diff --git a/backend/src/main/java/com/example/Piroin/project/domain/attendance/dto/AttendanceSlotRes.java b/backend/src/main/java/com/example/Piroin/project/domain/attendance/dto/AttendanceSlotRes.java index 44daaf8..ee842b1 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/attendance/dto/AttendanceSlotRes.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/attendance/dto/AttendanceSlotRes.java @@ -1,7 +1,6 @@ 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; @@ -9,13 +8,15 @@ @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; } - -} - +} \ No newline at end of file diff --git a/backend/src/main/java/com/example/Piroin/project/domain/attendance/repository/AttendanceCodeRepository.java b/backend/src/main/java/com/example/Piroin/project/domain/attendance/repository/AttendanceCodeRepository.java index 7666ce0..ac9f9e9 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/attendance/repository/AttendanceCodeRepository.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/attendance/repository/AttendanceCodeRepository.java @@ -21,8 +21,6 @@ select count(ac) """) int countByStudySessionDate(@Param("date") LocalDate date); - List findByIsExpiredFalse(); - // [추가] 모든 활성화된 코드를 한 번에 만료 처리 (벌크 연산) @Modifying @@ -31,12 +29,15 @@ select count(ac) Optional findFirstByIsExpiredFalseOrderByIdDesc(); - Optional findByCodeAndIsExpiredFalse(String code); - List findByStudySessionId(Long studySessionId); Optional findByCodeAndStudySessionId(String code, Long studySessionId); + // 특정 날짜에 발급된 코드 개수 조회 + long countByAttendanceDate(String attendanceDate); + + // 만료되지 않은 코드 목록 조회 + List findByIsExpiredFalse(); } diff --git a/backend/src/main/java/com/example/Piroin/project/domain/attendance/repository/AttendanceRepository.java b/backend/src/main/java/com/example/Piroin/project/domain/attendance/repository/AttendanceRepository.java index fcbad59..dba40db 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/attendance/repository/AttendanceRepository.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/attendance/repository/AttendanceRepository.java @@ -11,7 +11,7 @@ public interface AttendanceRepository extends JpaRepository { - List findByUserId(Long userId); + // List findByUserId(Long userId); Optional findByUserIdAndStudySessionId(Long userId, Long studySessionId); @@ -24,6 +24,15 @@ public interface AttendanceRepository extends JpaRepository { List findByUserIdAndStudySessionSessionDate(Long userId, LocalDate date); int countByUserAndStatusFalse(User user); + + // 1. 특정 출석 코드 ID에 해당하는 결석 데이터 조회 + List findByAttendanceCodeIdAndStatusFalse(Integer attendanceCodeId); + + // 2. 특정 유저 ID와 출석 코드의 날짜 조건으로 조회 (엔티티 그래프 참조: attendanceCode.attendanceDate) + List findByUserIdAndAttendanceCodeAttendanceDate(Integer userId, String attendanceDate); + + // 3. 특정 유저의 모든 출석 데이터 조회 + List findByUserId(Integer userId); } diff --git a/backend/src/main/java/com/example/Piroin/project/domain/attendance/service/AttendanceService.java b/backend/src/main/java/com/example/Piroin/project/domain/attendance/service/AttendanceService.java index 690e97d..d6cd72d 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/attendance/service/AttendanceService.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/attendance/service/AttendanceService.java @@ -48,48 +48,54 @@ public class AttendanceService { // 1. 출석 시작 코드 (출석코드 생성 함수) - // 출석 시작은 이제 date/order가 아니라 studySessionId를 받아야 함. @Transactional - public AttendanceCode generateCodeAndCreateAttendances(Long studySessionId) { + public AttendanceCode generateCodeAndCreateAttendances(Integer studySessionId) { // ID 타입 Long -> Integer 변경 + // 1. 세션 조회 (날짜 정보를 가져오기 위함) StudySession studySession = curriculumRepository.findById(studySessionId) .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 세션입니다.")); - LocalDate sessionDate = studySession.getDate(); + // 2. 세션의 날짜를 String으로 변환 (DB의 VARCHAR 타입과 매칭, 보통 "yyyy-MM-dd" 형태) + String sessionDateStr = studySession.getSessionDate().toString(); - int codeCountOfDay = attendanceCodeRepository.countByStudySessionDate(sessionDate); + // 3. 해당 날짜에 생성된 출석 코드 개수 조회 (Repository에 메서드 추가 필요) + long codeCountOfDay = attendanceCodeRepository.countByAttendanceDate(sessionDateStr); if (codeCountOfDay >= 3) { throw new IllegalStateException("하루에 최대 3회까지만 출석 코드를 생성할 수 있습니다."); } + // 4. 기존 활성화된 코드들 만료 처리 List activeCodes = attendanceCodeRepository.findByIsExpiredFalse(); - for (AttendanceCode activeCode : activeCodes) { activeCode.expire(); } + // 5. 4자리 랜덤 코드 생성 및 차수(Order) 계산 String code = String.valueOf(ThreadLocalRandom.current().nextInt(1000, 10000)); + String attendanceOrder = String.valueOf(codeCountOfDay + 1); // 1회차, 2회차, 3회차 + // 6. 새로운 AttendanceCode 생성 및 저장 AttendanceCode attendanceCode = AttendanceCode.builder() - .studySession(studySession) + .attendanceDate(sessionDateStr) + .attendanceOrder(attendanceOrder) .code(code) .isExpired(false) .build(); attendanceCodeRepository.save(attendanceCode); + // 7. 모든 MEMBER 유저에 대해 '현재 생성된 출석 코드' 기준 초기 출석 데이터 생성 List users = userRepository.findByRole(Role.MEMBER); for (User user : users) { - if (!attendanceRepository.existsByUserIdAndStudySessionId(user.getId(), studySessionId)) { - Attendance attendance = Attendance.builder() - .user(user) - .studySession(studySession) - .status(false) - .build(); - - attendanceRepository.save(attendance); - } + // 방금 새로운 출석 코드가 발급되었으므로, 해당 코드에 대한 출석 데이터는 항상 존재하지 않음 (중복 체크 생략 가능) + Attendance attendance = Attendance.builder() + .user(user) + .attendanceCode(attendanceCode) // studySession 대신 새로 만든 코드를 주입 + .status(false) + .build(); + + attendanceRepository.save(attendance); } return attendanceCode; @@ -146,17 +152,20 @@ public AttendanceMarkResponse markAttendance(Long userId, Long studySessionId, S // 4. 출석 코드 만료시키기. @Transactional public String expireActiveAttendanceCode() { + // 1. 활성화된 최신 출석 코드 조회 AttendanceCode activeCode = attendanceCodeRepository .findFirstByIsExpiredFalseOrderByIdDesc() .orElseThrow(() -> new IllegalStateException("현재 활성화된 출석 코드가 없습니다.")); + // 2. 코드 만료 처리 activeCode.expire(); - Long studySessionId = activeCode.getStudySession().getId(); - + // 3. 변경된 구조: 만료된 '출석 코드의 ID'를 기반으로 결석자(status = false) 조회 + Integer attendanceCodeId = activeCode.getId(); List absents = - attendanceRepository.findByStudySessionIdAndStatusFalse(studySessionId); + attendanceRepository.findByAttendanceCodeIdAndStatusFalse(attendanceCodeId); + // 4. 결석자 대상 보증금 재계산 (User ID 타입 Integer 반영) for (Attendance attendance : absents) { depositService.recalculateDeposit(attendance.getUser().getId()); } @@ -166,38 +175,44 @@ public String expireActiveAttendanceCode() { // 5. 유저의 특정 날짜의 출석 현황을 조회하는 함수 - public List findByUserIdAndDate(Long userId, LocalDate date) { + public List findByUserIdAndDate(Integer userId, LocalDate date) { // Long -> Integer + // DB의 VARCHAR(255) 날짜 포맷과 맞추기 위해 String으로 변환 (예: "2026-05-17") + String dateStr = date.toString(); + // 변경된 구조: User ID와 AttendanceCode의 날짜 조건으로 조회 List attendances = - attendanceRepository.findByUserIdAndStudySessionSessionDate(userId, date); + attendanceRepository.findByUserIdAndAttendanceCodeAttendanceDate(userId, dateStr); return attendances.stream() .map(attendance -> new AttendanceSlotRes( - attendance.getStudySession().getId(), // 임시로 세션 ID를 슬롯 식별값으로 사용 - attendance.getStatus() // Boolean getter는 isStatus()가 아니라 getStatus() + attendance.getAttendanceCode().getId(), // 세션 ID 대신 출석 코드 ID를 슬롯 식별값으로 사용 + attendance.getStatus() )) - .sorted(Comparator.comparing(AttendanceSlotRes::getStudySessionId)) + .sorted(Comparator.comparing(AttendanceSlotRes::getAttendanceCodeId)) // 정렬 기준 변경 .toList(); } - public List findByUserId(Long userId) { + // 6. 유저의 전체 출석 현황을 날짜별로 묶어서 조회하는 함수 + public List findByUserId(Integer userId) { // Long -> Integer List attendances = attendanceRepository.findByUserId(userId); - Map> grouped = attendances.stream() + // 변경된 구조: AttendanceCode에 저장된 String 날짜를 기준으로 그룹화(groupingBy) + Map> grouped = attendances.stream() .collect(Collectors.groupingBy( - attendance -> attendance.getStudySession().getSessionDate() + attendance -> attendance.getAttendanceCode().getAttendanceDate() )); return grouped.entrySet().stream() .map(entry -> { - LocalDate date = entry.getKey(); + // String으로 정렬/그룹화된 키를 다시 LocalDate 객체로 변환하여 DTO에 주입 + LocalDate date = LocalDate.parse(entry.getKey()); List slots = entry.getValue().stream() .map(attendance -> new AttendanceSlotRes( - attendance.getStudySession().getId(), + attendance.getAttendanceCode().getId(), attendance.getStatus() )) - .sorted(Comparator.comparing(AttendanceSlotRes::getStudySessionId)) + .sorted(Comparator.comparing(AttendanceSlotRes::getAttendanceCodeId)) .toList(); AttendanceStatusRes dto = new AttendanceStatusRes(); @@ -206,7 +221,7 @@ public List findByUserId(Long userId) { return dto; }) - .sorted(Comparator.comparing(AttendanceStatusRes::getDate).reversed()) + .sorted(Comparator.comparing(AttendanceStatusRes::getDate).reversed()) // 최신날짜 순 정렬 .toList(); } diff --git a/backend/src/main/java/com/example/Piroin/project/domain/curriculum/repository/CurriculumRepository.java b/backend/src/main/java/com/example/Piroin/project/domain/curriculum/repository/CurriculumRepository.java index 8989cd9..aea0b3f 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/curriculum/repository/CurriculumRepository.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/curriculum/repository/CurriculumRepository.java @@ -11,7 +11,7 @@ Q&A 서비스에서 세션 존재 여부 확인 시 사용 JpaRepository<엔티티 타입, PK 타입> 을 상속하면 findById, save, delete 등 기본 메서드가 자동으로 제공 */ -public interface CurriculumRepository extends JpaRepository { +public interface CurriculumRepository extends JpaRepository { List findByStatusOrderBySessionDateAscDayPartAsc(SessionStatus status); List findByStatusOrderBySessionDateDescDayPartDesc(SessionStatus status); diff --git a/backend/src/main/java/com/example/Piroin/project/domain/curriculum/service/CurriculumService.java b/backend/src/main/java/com/example/Piroin/project/domain/curriculum/service/CurriculumService.java index 6599d79..f53e27c 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/curriculum/service/CurriculumService.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/curriculum/service/CurriculumService.java @@ -50,7 +50,7 @@ public CurriculumResDTO.CreateSessionRes createSession(CurriculumReqDTO.CreateSe @Transactional public CurriculumResDTO.UpdateSessionRes updateSession(Long sessionId, CurriculumReqDTO.UpdateSessionReq req) { - StudySession session = curriculumRepository.findById(sessionId) + StudySession session = curriculumRepository.findById(Math.toIntExact(sessionId)) .orElseThrow(() -> new CurriculumException(HttpStatus.NOT_FOUND, "세션을 찾을 수 없습니다.")); session.update(req.getGeneration(), req.getWeek(), req.getSessionDate(), req.getDayPart(), @@ -63,7 +63,7 @@ public CurriculumResDTO.UpdateSessionRes updateSession(Long sessionId, Curriculu @Transactional public void deleteSession(Long sessionId) { - StudySession session = curriculumRepository.findById(sessionId) + StudySession session = curriculumRepository.findById(Math.toIntExact(sessionId)) .orElseThrow(() -> new CurriculumException(HttpStatus.NOT_FOUND, "세션을 찾을 수 없습니다.")); curriculumRepository.delete(session); diff --git a/backend/src/main/java/com/example/Piroin/project/domain/question/service/QuestionService.java b/backend/src/main/java/com/example/Piroin/project/domain/question/service/QuestionService.java index f0ef840..598f7e0 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/question/service/QuestionService.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/question/service/QuestionService.java @@ -171,7 +171,7 @@ private QuestionResDTO.UnderstandingResponseResult toUnderstandingResponseResult } private StudySession findSession(Long sessionId) { - return curriculumRepository.findById(sessionId) + return curriculumRepository.findById(Math.toIntExact(sessionId)) .orElseThrow(() -> new QuestionException(HttpStatus.NOT_FOUND, "세션을 찾을 수 없습니다.")); } diff --git a/backend/src/main/resources/db/migration/V1__init.sql b/backend/src/main/resources/db/migration/V1__init.sql index e35b28e..a59d51d 100644 --- a/backend/src/main/resources/db/migration/V1__init.sql +++ b/backend/src/main/resources/db/migration/V1__init.sql @@ -45,7 +45,7 @@ CREATE TABLE assignment ( ); CREATE TABLE assignment_item ( - id SERIAL NOT NULL, + id BIGSERIAL NOT NULL, user_id INT NOT NULL, -- FK 대상이므로 SERIAL에서 INT로 수정 assignment_id INT NOT NULL, -- FK 대상이므로 SERIAL에서 INT로 수정 submitted VARCHAR(20) NOT NULL DEFAULT 'SUCCESS', From bdc38d34c20955e0d764f142be0c2d74292b8fc2 Mon Sep 17 00:00:00 2001 From: lilyyang0077 Date: Sun, 17 May 2026 18:44:52 +0900 Subject: [PATCH 08/12] =?UTF-8?q?feat:=20=EC=B2=B4=ED=81=AC=EC=84=AC=20?= =?UTF-8?q?=EC=98=A4=EB=A5=98=20=EB=B0=A9=EC=A7=80=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=A3=BC=EC=84=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/main/resources/application.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/src/main/resources/application.yml b/backend/src/main/resources/application.yml index d094235..a839863 100644 --- a/backend/src/main/resources/application.yml +++ b/backend/src/main/resources/application.yml @@ -10,6 +10,7 @@ spring: driver-class-name: org.postgresql.Driver flyway: +# repair-on-migrate: true enabled: true jpa: From 188cf9c3068316907a3df48dc5cd9cfaad7625bf Mon Sep 17 00:00:00 2001 From: lilyyang0077 Date: Sun, 17 May 2026 18:45:44 +0900 Subject: [PATCH 09/12] =?UTF-8?q?refactor:=20userId=20=ED=83=80=EC=9E=85?= =?UTF-8?q?=20Integer=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../example/Piroin/project/domain/user/dto/LoginResponse.java | 2 +- .../com/example/Piroin/project/domain/user/entity/User.java | 2 +- .../Piroin/project/domain/user/repository/UserRepository.java | 2 +- .../java/com/example/Piroin/project/global/jwt/JwtUtil.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/src/main/java/com/example/Piroin/project/domain/user/dto/LoginResponse.java b/backend/src/main/java/com/example/Piroin/project/domain/user/dto/LoginResponse.java index ae3d4fe..3432065 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/user/dto/LoginResponse.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/user/dto/LoginResponse.java @@ -8,7 +8,7 @@ public class LoginResponse { @Schema(description = "유저 고유 ID", example = "1") - private Long id; + private Integer id; @Schema(description = "유저 이름", example = "김피로") private String name; diff --git a/backend/src/main/java/com/example/Piroin/project/domain/user/entity/User.java b/backend/src/main/java/com/example/Piroin/project/domain/user/entity/User.java index 376d6ea..56ed90c 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/user/entity/User.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/user/entity/User.java @@ -14,7 +14,7 @@ public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; + private Integer id; @Column(nullable = false) private String password; diff --git a/backend/src/main/java/com/example/Piroin/project/domain/user/repository/UserRepository.java b/backend/src/main/java/com/example/Piroin/project/domain/user/repository/UserRepository.java index 3d05f24..166c695 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/user/repository/UserRepository.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/user/repository/UserRepository.java @@ -8,7 +8,7 @@ import java.util.List; import java.util.Optional; -public interface UserRepository extends JpaRepository { +public interface UserRepository extends JpaRepository { Optional findByName(String name); List findByRole(Role role); diff --git a/backend/src/main/java/com/example/Piroin/project/global/jwt/JwtUtil.java b/backend/src/main/java/com/example/Piroin/project/global/jwt/JwtUtil.java index 280accd..7b9b09e 100644 --- a/backend/src/main/java/com/example/Piroin/project/global/jwt/JwtUtil.java +++ b/backend/src/main/java/com/example/Piroin/project/global/jwt/JwtUtil.java @@ -24,7 +24,7 @@ public JwtUtil(@Value("${jwt.secret}") String secret, this.expiration = expiration; } - public String generateToken(Long userId, String role) { + public String generateToken(Integer userId, String role) { return Jwts.builder() .subject(String.valueOf(userId)) .claim("role", role) From 7770ba32e5a755c0b0fa2ac81ce3f73ac794adbd Mon Sep 17 00:00:00 2001 From: lilyyang0077 Date: Sun, 17 May 2026 18:46:20 +0900 Subject: [PATCH 10/12] =?UTF-8?q?fix:=20=ED=83=80=EC=9E=85=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EC=B9=BC=EB=9F=BC?= =?UTF-8?q?=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../assignment/entity/AssignmentItem.java | 2 +- .../controller/AdminAttendanceController.java | 16 +++++-- .../controller/AttendanceController.java | 4 +- .../attendance/dto/MarkAttendanceReq.java | 4 +- .../repository/AttendanceCodeRepository.java | 14 ++---- .../repository/AttendanceRepository.java | 22 ++++++--- .../attendance/service/AttendanceService.java | 48 +++++++++---------- .../curriculum/dto/CurriculumReqDTO.java | 2 +- .../domain/deposit/entity/Deposit.java | 2 +- .../deposit/service/DepositService.java | 2 +- .../controller/QuestionController.java | 4 +- .../question/service/QuestionService.java | 6 +-- .../main/resources/db/migration/V1__init.sql | 21 ++++---- 13 files changed, 79 insertions(+), 68 deletions(-) diff --git a/backend/src/main/java/com/example/Piroin/project/domain/assignment/entity/AssignmentItem.java b/backend/src/main/java/com/example/Piroin/project/domain/assignment/entity/AssignmentItem.java index 1203f55..b81e9d3 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/assignment/entity/AssignmentItem.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/assignment/entity/AssignmentItem.java @@ -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) diff --git a/backend/src/main/java/com/example/Piroin/project/domain/attendance/controller/AdminAttendanceController.java b/backend/src/main/java/com/example/Piroin/project/domain/attendance/controller/AdminAttendanceController.java index aef7c78..7416663 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/attendance/controller/AdminAttendanceController.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/attendance/controller/AdminAttendanceController.java @@ -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; @@ -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(Math.toIntExact(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); } @@ -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); } diff --git a/backend/src/main/java/com/example/Piroin/project/domain/attendance/controller/AttendanceController.java b/backend/src/main/java/com/example/Piroin/project/domain/attendance/controller/AttendanceController.java index 79eb44b..92222e4 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/attendance/controller/AttendanceController.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/attendance/controller/AttendanceController.java @@ -44,11 +44,11 @@ public ApiResponse 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(), req.getCode() ); diff --git a/backend/src/main/java/com/example/Piroin/project/domain/attendance/dto/MarkAttendanceReq.java b/backend/src/main/java/com/example/Piroin/project/domain/attendance/dto/MarkAttendanceReq.java index f815067..d777a57 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/attendance/dto/MarkAttendanceReq.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/attendance/dto/MarkAttendanceReq.java @@ -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; diff --git a/backend/src/main/java/com/example/Piroin/project/domain/attendance/repository/AttendanceCodeRepository.java b/backend/src/main/java/com/example/Piroin/project/domain/attendance/repository/AttendanceCodeRepository.java index ac9f9e9..6002531 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/attendance/repository/AttendanceCodeRepository.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/attendance/repository/AttendanceCodeRepository.java @@ -14,14 +14,6 @@ public interface AttendanceCodeRepository extends JpaRepository { - @Query(""" - select count(ac) - from AttendanceCode ac - where ac.studySession.sessionDate = :date - """) - int countByStudySessionDate(@Param("date") LocalDate date); - - // [추가] 모든 활성화된 코드를 한 번에 만료 처리 (벌크 연산) @Modifying @Query("update AttendanceCode ac set ac.isExpired = true where ac.isExpired = false") @@ -29,15 +21,15 @@ select count(ac) Optional findFirstByIsExpiredFalseOrderByIdDesc(); - List findByStudySessionId(Long studySessionId); - - Optional findByCodeAndStudySessionId(String code, Long studySessionId); +// Optional findByCodeAndStudySessionId(String code, Long studySessionId); // 특정 날짜에 발급된 코드 개수 조회 long countByAttendanceDate(String attendanceDate); // 만료되지 않은 코드 목록 조회 List findByIsExpiredFalse(); + + Optional findByCode(String code); } diff --git a/backend/src/main/java/com/example/Piroin/project/domain/attendance/repository/AttendanceRepository.java b/backend/src/main/java/com/example/Piroin/project/domain/attendance/repository/AttendanceRepository.java index dba40db..0469e2b 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/attendance/repository/AttendanceRepository.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/attendance/repository/AttendanceRepository.java @@ -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; @@ -13,15 +16,13 @@ public interface AttendanceRepository extends JpaRepository { // List findByUserId(Long userId); - Optional findByUserIdAndStudySessionId(Long userId, Long studySessionId); + //Optional findByUserIdAndStudySessionId(Integer userId, Long studySessionId); - boolean existsByUserIdAndStudySessionId(Long userId, Long studySessionId); - List findByStudySessionId(Long studySessionId); + // 연관관계 필드명이 attendanceCode 라면 내부 ID인 Id를 조합하여 명명 + Optional findByUserIdAndAttendanceCodeId(Integer userId, Long attendanceCodeId); - List findByStudySessionIdAndStatusFalse(Long studySessionId); - - List findByUserIdAndStudySessionSessionDate(Long userId, LocalDate date); + //List findByUserIdAndStudySessionSessionDate(Integer userId, LocalDate date); int countByUserAndStatusFalse(User user); @@ -29,10 +30,17 @@ public interface AttendanceRepository extends JpaRepository { List findByAttendanceCodeIdAndStatusFalse(Integer attendanceCodeId); // 2. 특정 유저 ID와 출석 코드의 날짜 조건으로 조회 (엔티티 그래프 참조: attendanceCode.attendanceDate) - List findByUserIdAndAttendanceCodeAttendanceDate(Integer userId, String attendanceDate); + @Query("SELECT a FROM Attendance a WHERE a.user.id = :userId AND a.attendanceCode.attendanceDate = :attendanceDate") + List findByUserIdAndDate(@Param("userId") Integer userId, @Param("attendanceDate") String attendanceDate); // 3. 특정 유저의 모든 출석 데이터 조회 List findByUserId(Integer userId); + + // 특정 날짜에 발급된 출석 코드의 개수를 세는 메서드 + //long countByAttendanceDate(String attendanceDate); + + // 현재 만료되지 않은(활성화된) 출석 코드 목록을 가져오는 메서드 + //List findByIsExpiredFalse(); } diff --git a/backend/src/main/java/com/example/Piroin/project/domain/attendance/service/AttendanceService.java b/backend/src/main/java/com/example/Piroin/project/domain/attendance/service/AttendanceService.java index d6cd72d..c8df950 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/attendance/service/AttendanceService.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/attendance/service/AttendanceService.java @@ -49,34 +49,30 @@ public class AttendanceService { // 1. 출석 시작 코드 (출석코드 생성 함수) @Transactional - public AttendanceCode generateCodeAndCreateAttendances(Integer studySessionId) { // ID 타입 Long -> Integer 변경 - // 1. 세션 조회 (날짜 정보를 가져오기 위함) - StudySession studySession = curriculumRepository.findById(studySessionId) - .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 세션입니다.")); + public AttendanceCode generateCodeAndCreateAttendances(String dateStr) { // [수정] 세션 ID 대신 날짜를 직접 받음 - // 2. 세션의 날짜를 String으로 변환 (DB의 VARCHAR 타입과 매칭, 보통 "yyyy-MM-dd" 형태) - String sessionDateStr = studySession.getSessionDate().toString(); + // 1. [삭제] 더 이상 세션을 조회해서 날짜를 파싱할 필요가 없습니다. (curriculumRepository 조회 제거) - // 3. 해당 날짜에 생성된 출석 코드 개수 조회 (Repository에 메서드 추가 필요) - long codeCountOfDay = attendanceCodeRepository.countByAttendanceDate(sessionDateStr); + // 2. 해당 날짜에 생성된 출석 코드 개수 조회 + long codeCountOfDay = attendanceCodeRepository.countByAttendanceDate(dateStr); if (codeCountOfDay >= 3) { throw new IllegalStateException("하루에 최대 3회까지만 출석 코드를 생성할 수 있습니다."); } - // 4. 기존 활성화된 코드들 만료 처리 + // 3. 기존 활성화된 코드들 만료 처리 List activeCodes = attendanceCodeRepository.findByIsExpiredFalse(); for (AttendanceCode activeCode : activeCodes) { activeCode.expire(); } - // 5. 4자리 랜덤 코드 생성 및 차수(Order) 계산 + // 4. 4자리 랜덤 코드 생성 및 차수(Order) 계산 String code = String.valueOf(ThreadLocalRandom.current().nextInt(1000, 10000)); String attendanceOrder = String.valueOf(codeCountOfDay + 1); // 1회차, 2회차, 3회차 - // 6. 새로운 AttendanceCode 생성 및 저장 + // 5. 새로운 AttendanceCode 생성 및 저장 AttendanceCode attendanceCode = AttendanceCode.builder() - .attendanceDate(sessionDateStr) + .attendanceDate(dateStr) // [수정] 파라미터로 받은 날짜 주입 .attendanceOrder(attendanceOrder) .code(code) .isExpired(false) @@ -84,14 +80,14 @@ public AttendanceCode generateCodeAndCreateAttendances(Integer studySessionId) { attendanceCodeRepository.save(attendanceCode); - // 7. 모든 MEMBER 유저에 대해 '현재 생성된 출석 코드' 기준 초기 출석 데이터 생성 + // 6. 모든 MEMBER 유저에 대해 '현재 생성된 출석 코드' 기준 초기 출석 데이터 생성 List users = userRepository.findByRole(Role.MEMBER); for (User user : users) { - // 방금 새로운 출석 코드가 발급되었으므로, 해당 코드에 대한 출석 데이터는 항상 존재하지 않음 (중복 체크 생략 가능) + // [확인] 이미 완벽하게 studySession 대신 attendanceCode를 주입하도록 잘 짜두셨습니다! Attendance attendance = Attendance.builder() .user(user) - .attendanceCode(attendanceCode) // studySession 대신 새로 만든 코드를 주입 + .attendanceCode(attendanceCode) .status(false) .build(); @@ -110,13 +106,13 @@ public Optional getActiveAttendanceCode() { // 3. 출석 체크 @Transactional - public AttendanceMarkResponse markAttendance(Long userId, Long studySessionId, String inputCode) { - // 사용자가 입력한 코드가 이 세션의 코드가 맞는지 확인 + public AttendanceMarkResponse markAttendance(Integer userId, String inputCode) { + // 1. [수정] 오직 사용자가 입력한 코드를 기반으로 출석 코드 정보를 조회합니다. AttendanceCode code = attendanceCodeRepository - .findByCodeAndStudySessionId(inputCode, studySessionId) + .findByCode(inputCode) .orElse(null); - // 입력한 출석 코드가 해당 세션의 출석 코드와 일치하지 않는 경우 + // 입력한 출석 코드가 DB에 존재하지 않는 경우 if (code == null) { return AttendanceMarkResponse.invalidCode(); } @@ -126,11 +122,14 @@ public AttendanceMarkResponse markAttendance(Long userId, Long studySessionId, S return AttendanceMarkResponse.codeExpired(); } + // 2. [수정] 이제 Attendance도 studySessionId 대신 AttendanceCode와의 연관관계(예: attendanceCodeId) + // 혹은 조회된 code의 날짜/차수 정보를 기반으로 기존 출석 기록을 찾아야 합니다. + // (여기서는 이전 답변 시나리오 1인 'attendanceCodeId'로 매핑했다고 가정했을 때의 예시입니다.) Attendance attendance = attendanceRepository - .findByUserIdAndStudySessionId(userId, studySessionId) + .findByUserIdAndAttendanceCodeId(userId, Long.valueOf(code.getId())) .orElse(null); - // 해당 사용자와 세션에 대한 출석 기록이 존재하지 않는 경우 + // 해당 사용자와 출석 코드에 대한 출석 기록이 존재하지 않는 경우 if (attendance == null) { return AttendanceMarkResponse.error("출석 정보를 찾을 수 없습니다."); } @@ -139,11 +138,12 @@ public AttendanceMarkResponse markAttendance(Long userId, Long studySessionId, S if (Boolean.TRUE.equals(attendance.getStatus())) { return AttendanceMarkResponse.alreadyMarked(); } + // 출석 상태를 출석 완료(true)로 변경 attendance.updateStatus(true); // 출석 상태 변경 후 보증금 재계산 - depositService.recalculateDeposit(userId); // 아직 recalculateDeposit 부분 생성 안 해서 오류 나는 게 정상. + depositService.recalculateDeposit(userId); // 아직 생성 안 하신 부분 오류 패스! return AttendanceMarkResponse.success(); } @@ -181,7 +181,7 @@ public List findByUserIdAndDate(Integer userId, LocalDate dat // 변경된 구조: User ID와 AttendanceCode의 날짜 조건으로 조회 List attendances = - attendanceRepository.findByUserIdAndAttendanceCodeAttendanceDate(userId, dateStr); + attendanceRepository.findByUserIdAndDate(userId, dateStr); return attendances.stream() .map(attendance -> new AttendanceSlotRes( @@ -228,7 +228,7 @@ public List findByUserId(Integer userId) { // Long -> Integ // 6. 유저 상태 변경 (관리자) // 컨트롤러 부분은 출석만 받는데 여기는 출석&과제 둘 다 받아서 추후에 수정 예정 @Transactional - public boolean updateUserStatus(Long userId, UpdateUserStatusReq req) { + public boolean updateUserStatus(Integer userId, UpdateUserStatusReq req) { boolean updated = false; // 출석 상태 변경 코드 diff --git a/backend/src/main/java/com/example/Piroin/project/domain/curriculum/dto/CurriculumReqDTO.java b/backend/src/main/java/com/example/Piroin/project/domain/curriculum/dto/CurriculumReqDTO.java index 03e03be..8fe8be1 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/curriculum/dto/CurriculumReqDTO.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/curriculum/dto/CurriculumReqDTO.java @@ -12,7 +12,7 @@ public class CurriculumReqDTO { @Getter @NoArgsConstructor public static class CreateSessionReq { - private Long userId; + private Integer userId; private Integer generation; private Long week; private LocalDate sessionDate; diff --git a/backend/src/main/java/com/example/Piroin/project/domain/deposit/entity/Deposit.java b/backend/src/main/java/com/example/Piroin/project/domain/deposit/entity/Deposit.java index 79cea9e..2fee4c3 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/deposit/entity/Deposit.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/deposit/entity/Deposit.java @@ -22,7 +22,7 @@ public class Deposit { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; + private Integer id; @OneToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id", nullable = false) diff --git a/backend/src/main/java/com/example/Piroin/project/domain/deposit/service/DepositService.java b/backend/src/main/java/com/example/Piroin/project/domain/deposit/service/DepositService.java index 3534bd2..3974479 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/deposit/service/DepositService.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/deposit/service/DepositService.java @@ -20,7 +20,7 @@ public class DepositService { private final AttendanceRepository attendanceRepository; @Transactional - public void recalculateDeposit(Long userId) { + public void recalculateDeposit(Integer userId) { User user = userRepository.findById(userId) .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 유저입니다.")); diff --git a/backend/src/main/java/com/example/Piroin/project/domain/question/controller/QuestionController.java b/backend/src/main/java/com/example/Piroin/project/domain/question/controller/QuestionController.java index 4678d0e..0eca7f2 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/question/controller/QuestionController.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/question/controller/QuestionController.java @@ -37,7 +37,7 @@ public ResponseEntity> r @PathVariable Long sessionId, @PathVariable Long checkId, @RequestBody QuestionReqDTO.UnderstandingResponseReq request, - @AuthenticationPrincipal Long userId + @AuthenticationPrincipal Integer userId ) { QuestionResDTO.UnderstandingResponseResult response = questionService.respondUnderstandingCheck(sessionId, checkId, request, userId); @@ -51,7 +51,7 @@ public ResponseEntity> r public ResponseEntity> createQuestion( @PathVariable Long sessionId, @RequestBody QuestionReqDTO.CreateReq request, - @AuthenticationPrincipal Long userId + @AuthenticationPrincipal Integer userId ) { QuestionResDTO.CreateRes response = questionService.createQuestion(sessionId, request, userId); diff --git a/backend/src/main/java/com/example/Piroin/project/domain/question/service/QuestionService.java b/backend/src/main/java/com/example/Piroin/project/domain/question/service/QuestionService.java index 598f7e0..037b929 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/question/service/QuestionService.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/question/service/QuestionService.java @@ -59,7 +59,7 @@ public QuestionResDTO.UnderstandingResponseResult respondUnderstandingCheck( Long sessionId, Long checkId, QuestionReqDTO.UnderstandingResponseReq request, - Long userId + Integer userId ) { if (request == null || request.getChoice() == null) { throw new IllegalArgumentException("이해도 응답 선택지는 필수입니다."); @@ -85,7 +85,7 @@ public QuestionResDTO.UnderstandingResponseResult respondUnderstandingCheck( public QuestionResDTO.CreateRes createQuestion( Long sessionId, QuestionReqDTO.CreateReq request, - Long userId + Integer userId ) { User loginUser = findLoginUser(userId); @@ -107,7 +107,7 @@ public QuestionResDTO.CreateRes createQuestion( return QuestionResDTO.CreateRes.from(questionRepository.save(question)); } - private User findLoginUser(Long userId) { + private User findLoginUser(Integer userId) { if (userId == null) { throw new IllegalStateException("로그인이 필요합니다."); } diff --git a/backend/src/main/resources/db/migration/V1__init.sql b/backend/src/main/resources/db/migration/V1__init.sql index a59d51d..7440bb9 100644 --- a/backend/src/main/resources/db/migration/V1__init.sql +++ b/backend/src/main/resources/db/migration/V1__init.sql @@ -13,10 +13,10 @@ CREATE TABLE users ( ); CREATE TABLE study_session ( - id SERIAL NOT NULL, + id BIGSERIAL NOT NULL, created_by INT NOT NULL, generation INT NULL, - week INT NOT NULL, + week BIGINT NOT NULL, session_date DATE NOT NULL, day_part VARCHAR(10) NOT NULL, title VARCHAR(255) NOT NULL, @@ -45,7 +45,7 @@ CREATE TABLE assignment ( ); CREATE TABLE assignment_item ( - id BIGSERIAL NOT NULL, + id SERIAL NOT NULL, user_id INT NOT NULL, -- FK 대상이므로 SERIAL에서 INT로 수정 assignment_id INT NOT NULL, -- FK 대상이므로 SERIAL에서 INT로 수정 submitted VARCHAR(20) NOT NULL DEFAULT 'SUCCESS', @@ -72,8 +72,8 @@ CREATE TABLE attendance ( ); CREATE TABLE question ( - id SERIAL NOT NULL, - session_id INT NOT NULL, + id BIGSERIAL NOT NULL, + session_id BIGINT NOT NULL, user_id INT NOT NULL, content VARCHAR(1000) NOT NULL, image_url VARCHAR(1000) NULL, @@ -86,10 +86,10 @@ CREATE TABLE question ( ); CREATE TABLE question_comment ( - id SERIAL NOT NULL, - question_id INT NOT NULL, + id BIGSERIAL NOT NULL, + question_id BIGINT NOT NULL, user_id INT NOT NULL, - parent_comment_id INT NULL, + parent_comment_id BIGINT NULL, content VARCHAR(1000) NOT NULL, image_url VARCHAR(1000) NULL, created_at TIMESTAMP NOT NULL, @@ -99,9 +99,9 @@ CREATE TABLE question_comment ( ); CREATE TABLE question_anonymous_identity ( - id SERIAL NOT NULL, + id BIGSERIAL NOT NULL, user_id INT NOT NULL, - question_id INT NOT NULL, + question_id BIGINT NOT NULL, anonymous_no INT NOT NULL DEFAULT 1, created_at TIMESTAMP NOT NULL, CONSTRAINT PK_QUESTION_ANONYMOUS_IDENTITY PRIMARY KEY (id) @@ -132,6 +132,7 @@ CREATE TABLE understanding_response ( user_id BIGINT NOT NULL, choice VARCHAR(20) NOT NULL, created_at TIMESTAMP NOT NULL, + updated_at TIMESTAMP NOT NULL, CONSTRAINT PK_UNDERSTANDING_RESPONSE PRIMARY KEY (id), CONSTRAINT CHK_UNDERSTANDING_RESPONSE_CHOICE CHECK (choice IN ('UNDERSTOOD', 'NOT_UNDERSTOOD')) ); From 5fcdee5c6341393102efa0a00fff7245e21644de Mon Sep 17 00:00:00 2001 From: lilyyang0077 Date: Sun, 17 May 2026 19:14:20 +0900 Subject: [PATCH 11/12] =?UTF-8?q?refactor:=20userId=20=EC=A0=84=EB=B6=80?= =?UTF-8?q?=20BIGINT(Long)=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/AttendanceController.java | 2 +- .../repository/AttendanceRepository.java | 4 ++-- .../attendance/service/AttendanceService.java | 6 +++--- .../domain/curriculum/dto/CurriculumReqDTO.java | 2 +- .../domain/deposit/service/DepositService.java | 2 +- .../domain/question/service/QuestionService.java | 2 +- .../project/domain/user/dto/LoginResponse.java | 2 +- .../Piroin/project/domain/user/entity/User.java | 2 +- .../domain/user/repository/UserRepository.java | 2 +- .../Piroin/project/global/jwt/JwtUtil.java | 2 +- .../src/main/resources/db/migration/V1__init.sql | 16 ++++++++-------- 11 files changed, 21 insertions(+), 21 deletions(-) diff --git a/backend/src/main/java/com/example/Piroin/project/domain/attendance/controller/AttendanceController.java b/backend/src/main/java/com/example/Piroin/project/domain/attendance/controller/AttendanceController.java index 92222e4..477476a 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/attendance/controller/AttendanceController.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/attendance/controller/AttendanceController.java @@ -48,7 +48,7 @@ public ApiResponse markAttendance( ) { // [수정] 서비스 메서드 스펙 변경에 맞춰 req.getStudySessionId()를 제거했습니다. AttendanceMarkResponse response = attendanceService.markAttendance( - userId, + Long.valueOf(userId), req.getCode() ); diff --git a/backend/src/main/java/com/example/Piroin/project/domain/attendance/repository/AttendanceRepository.java b/backend/src/main/java/com/example/Piroin/project/domain/attendance/repository/AttendanceRepository.java index 0469e2b..f027731 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/attendance/repository/AttendanceRepository.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/attendance/repository/AttendanceRepository.java @@ -20,7 +20,7 @@ public interface AttendanceRepository extends JpaRepository { // 연관관계 필드명이 attendanceCode 라면 내부 ID인 Id를 조합하여 명명 - Optional findByUserIdAndAttendanceCodeId(Integer userId, Long attendanceCodeId); + Optional findByUserIdAndAttendanceCodeId(Long userId, Long attendanceCodeId); //List findByUserIdAndStudySessionSessionDate(Integer userId, LocalDate date); @@ -34,7 +34,7 @@ public interface AttendanceRepository extends JpaRepository { List findByUserIdAndDate(@Param("userId") Integer userId, @Param("attendanceDate") String attendanceDate); // 3. 특정 유저의 모든 출석 데이터 조회 - List findByUserId(Integer userId); + List findByUserId(Long userId); // 특정 날짜에 발급된 출석 코드의 개수를 세는 메서드 //long countByAttendanceDate(String attendanceDate); diff --git a/backend/src/main/java/com/example/Piroin/project/domain/attendance/service/AttendanceService.java b/backend/src/main/java/com/example/Piroin/project/domain/attendance/service/AttendanceService.java index c8df950..b0483a4 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/attendance/service/AttendanceService.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/attendance/service/AttendanceService.java @@ -106,7 +106,7 @@ public Optional getActiveAttendanceCode() { // 3. 출석 체크 @Transactional - public AttendanceMarkResponse markAttendance(Integer userId, String inputCode) { + public AttendanceMarkResponse markAttendance(Long userId, String inputCode) { // 1. [수정] 오직 사용자가 입력한 코드를 기반으로 출석 코드 정보를 조회합니다. AttendanceCode code = attendanceCodeRepository .findByCode(inputCode) @@ -194,7 +194,7 @@ public List findByUserIdAndDate(Integer userId, LocalDate dat // 6. 유저의 전체 출석 현황을 날짜별로 묶어서 조회하는 함수 public List findByUserId(Integer userId) { // Long -> Integer - List attendances = attendanceRepository.findByUserId(userId); + List attendances = attendanceRepository.findByUserId(Long.valueOf(userId)); // 변경된 구조: AttendanceCode에 저장된 String 날짜를 기준으로 그룹화(groupingBy) Map> grouped = attendances.stream() @@ -259,7 +259,7 @@ public boolean updateUserStatus(Integer userId, UpdateUserStatusReq req) { // 출석 변경 → 보증금 재계산 (과제 변경도 포함이 되어 있나..?) if (updated) { - depositService.recalculateDeposit(userId); + depositService.recalculateDeposit(Long.valueOf(userId)); } return updated; diff --git a/backend/src/main/java/com/example/Piroin/project/domain/curriculum/dto/CurriculumReqDTO.java b/backend/src/main/java/com/example/Piroin/project/domain/curriculum/dto/CurriculumReqDTO.java index 8fe8be1..03e03be 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/curriculum/dto/CurriculumReqDTO.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/curriculum/dto/CurriculumReqDTO.java @@ -12,7 +12,7 @@ public class CurriculumReqDTO { @Getter @NoArgsConstructor public static class CreateSessionReq { - private Integer userId; + private Long userId; private Integer generation; private Long week; private LocalDate sessionDate; diff --git a/backend/src/main/java/com/example/Piroin/project/domain/deposit/service/DepositService.java b/backend/src/main/java/com/example/Piroin/project/domain/deposit/service/DepositService.java index 3974479..3534bd2 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/deposit/service/DepositService.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/deposit/service/DepositService.java @@ -20,7 +20,7 @@ public class DepositService { private final AttendanceRepository attendanceRepository; @Transactional - public void recalculateDeposit(Integer userId) { + public void recalculateDeposit(Long userId) { User user = userRepository.findById(userId) .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 유저입니다.")); diff --git a/backend/src/main/java/com/example/Piroin/project/domain/question/service/QuestionService.java b/backend/src/main/java/com/example/Piroin/project/domain/question/service/QuestionService.java index 037b929..9b19549 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/question/service/QuestionService.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/question/service/QuestionService.java @@ -112,7 +112,7 @@ private User findLoginUser(Integer userId) { throw new IllegalStateException("로그인이 필요합니다."); } - return userRepository.findById(userId) + return userRepository.findById(Long.valueOf(userId)) .orElseThrow(() -> new QuestionException(HttpStatus.UNAUTHORIZED, "로그인 사용자를 찾을 수 없습니다.")); } diff --git a/backend/src/main/java/com/example/Piroin/project/domain/user/dto/LoginResponse.java b/backend/src/main/java/com/example/Piroin/project/domain/user/dto/LoginResponse.java index 3432065..ae3d4fe 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/user/dto/LoginResponse.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/user/dto/LoginResponse.java @@ -8,7 +8,7 @@ public class LoginResponse { @Schema(description = "유저 고유 ID", example = "1") - private Integer id; + private Long id; @Schema(description = "유저 이름", example = "김피로") private String name; diff --git a/backend/src/main/java/com/example/Piroin/project/domain/user/entity/User.java b/backend/src/main/java/com/example/Piroin/project/domain/user/entity/User.java index 56ed90c..376d6ea 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/user/entity/User.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/user/entity/User.java @@ -14,7 +14,7 @@ public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) - private Integer id; + private Long id; @Column(nullable = false) private String password; diff --git a/backend/src/main/java/com/example/Piroin/project/domain/user/repository/UserRepository.java b/backend/src/main/java/com/example/Piroin/project/domain/user/repository/UserRepository.java index 166c695..3d05f24 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/user/repository/UserRepository.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/user/repository/UserRepository.java @@ -8,7 +8,7 @@ import java.util.List; import java.util.Optional; -public interface UserRepository extends JpaRepository { +public interface UserRepository extends JpaRepository { Optional findByName(String name); List findByRole(Role role); diff --git a/backend/src/main/java/com/example/Piroin/project/global/jwt/JwtUtil.java b/backend/src/main/java/com/example/Piroin/project/global/jwt/JwtUtil.java index 7b9b09e..280accd 100644 --- a/backend/src/main/java/com/example/Piroin/project/global/jwt/JwtUtil.java +++ b/backend/src/main/java/com/example/Piroin/project/global/jwt/JwtUtil.java @@ -24,7 +24,7 @@ public JwtUtil(@Value("${jwt.secret}") String secret, this.expiration = expiration; } - public String generateToken(Integer userId, String role) { + public String generateToken(Long userId, String role) { return Jwts.builder() .subject(String.valueOf(userId)) .claim("role", role) diff --git a/backend/src/main/resources/db/migration/V1__init.sql b/backend/src/main/resources/db/migration/V1__init.sql index 7440bb9..00f9043 100644 --- a/backend/src/main/resources/db/migration/V1__init.sql +++ b/backend/src/main/resources/db/migration/V1__init.sql @@ -1,7 +1,7 @@ -- 1. 테이블 생성 (기본키 포함) CREATE TABLE users ( - id SERIAL NOT NULL, + id BIGSERIAL NOT NULL, password VARCHAR(100) NOT NULL, name VARCHAR(100) NOT NULL, email VARCHAR(255) NULL, @@ -14,7 +14,7 @@ CREATE TABLE users ( CREATE TABLE study_session ( id BIGSERIAL NOT NULL, - created_by INT NOT NULL, + created_by BIGINT NOT NULL, generation INT NULL, week BIGINT NOT NULL, session_date DATE NOT NULL, @@ -46,7 +46,7 @@ CREATE TABLE assignment ( CREATE TABLE assignment_item ( id SERIAL NOT NULL, - user_id INT NOT NULL, -- FK 대상이므로 SERIAL에서 INT로 수정 + user_id BIGINT NOT NULL, -- FK 대상이므로 SERIAL에서 INT로 수정 assignment_id INT NOT NULL, -- FK 대상이므로 SERIAL에서 INT로 수정 submitted VARCHAR(20) NOT NULL DEFAULT 'SUCCESS', CONSTRAINT PK_ASSIGNMENT_ITEM PRIMARY KEY (id), @@ -66,7 +66,7 @@ CREATE TABLE attendance_code ( CREATE TABLE attendance ( id SERIAL NOT NULL, attendance_code_id INT NOT NULL, - user_id INT NOT NULL, + user_id BIGINT NOT NULL, status BOOLEAN NOT NULL, -- TINYINT(1)에서 BOOLEAN으로 수정 CONSTRAINT PK_ATTENDANCE PRIMARY KEY (id) ); @@ -74,7 +74,7 @@ CREATE TABLE attendance ( CREATE TABLE question ( id BIGSERIAL NOT NULL, session_id BIGINT NOT NULL, - user_id INT NOT NULL, + user_id BIGINT NOT NULL, content VARCHAR(1000) NOT NULL, image_url VARCHAR(1000) NULL, is_resolved BOOLEAN NOT NULL, -- TINYINT(1)에서 BOOLEAN으로 수정 @@ -88,7 +88,7 @@ CREATE TABLE question ( CREATE TABLE question_comment ( id BIGSERIAL NOT NULL, question_id BIGINT NOT NULL, - user_id INT NOT NULL, + user_id BIGINT NOT NULL, parent_comment_id BIGINT NULL, content VARCHAR(1000) NOT NULL, image_url VARCHAR(1000) NULL, @@ -100,7 +100,7 @@ CREATE TABLE question_comment ( CREATE TABLE question_anonymous_identity ( id BIGSERIAL NOT NULL, - user_id INT NOT NULL, + user_id BIGINT NOT NULL, question_id BIGINT NOT NULL, anonymous_no INT NOT NULL DEFAULT 1, created_at TIMESTAMP NOT NULL, @@ -139,7 +139,7 @@ CREATE TABLE understanding_response ( CREATE TABLE deposit ( id SERIAL NOT NULL, - user_id INT NOT NULL, + user_id BIGINT NOT NULL, amount INT NOT NULL, descent_assignment INT NOT NULL, descent_attendance INT NOT NULL, From e2a08d45d96280d4e2eed38f6edf1bb3f4375b00 Mon Sep 17 00:00:00 2001 From: lilyyang0077 Date: Sun, 17 May 2026 19:34:33 +0900 Subject: [PATCH 12/12] =?UTF-8?q?fix:=20=ED=83=80=20=ED=8C=8C=ED=8A=B8(Que?= =?UTF-8?q?stion)=20ID=20=ED=83=80=EC=9E=85=20=EB=B6=88=EC=9D=BC=EC=B9=98?= =?UTF-8?q?=20=EC=BB=B4=ED=8C=8C=EC=9D=BC=20=EC=97=90=EB=9F=AC=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/question/controller/QuestionController.java | 10 +++++----- .../domain/question/service/QuestionService.java | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/backend/src/main/java/com/example/Piroin/project/domain/question/controller/QuestionController.java b/backend/src/main/java/com/example/Piroin/project/domain/question/controller/QuestionController.java index 0eca7f2..86f2b1c 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/question/controller/QuestionController.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/question/controller/QuestionController.java @@ -16,7 +16,7 @@ @RequiredArgsConstructor public class QuestionController { private final QuestionService questionService; - + // 질문 목록 + 이해도 조회 // GET /api/sessions/{sessionId}/questions?understandingIndex=0 @GetMapping("/{sessionId}/questions") @@ -26,7 +26,7 @@ public ResponseEntity> getQuest ) { QuestionResDTO.QuestionRoomResponse response = questionService.getQuestionRoom(sessionId, understandingIndex); - + return ResponseUtil.success(QuestionSuccessCode.QUESTION_ROOM_OK, response); } @@ -44,7 +44,7 @@ public ResponseEntity> r return ResponseUtil.success(QuestionSuccessCode.UNDERSTANDING_RESPONSE_OK, response); } - + // 질문 등록 // POST /api/sessions/{sessionId}/questions @PostMapping("/{sessionId}/questions") @@ -54,8 +54,8 @@ public ResponseEntity> createQuestion( @AuthenticationPrincipal Integer userId ) { QuestionResDTO.CreateRes response = - questionService.createQuestion(sessionId, request, userId); - + questionService.createQuestion(userId.longValue(), request, userId); + return ResponseUtil.success(QuestionSuccessCode.QUESTION_CREATED, response); } } diff --git a/backend/src/main/java/com/example/Piroin/project/domain/question/service/QuestionService.java b/backend/src/main/java/com/example/Piroin/project/domain/question/service/QuestionService.java index 9b19549..33ee8a3 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/question/service/QuestionService.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/question/service/QuestionService.java @@ -171,7 +171,7 @@ private QuestionResDTO.UnderstandingResponseResult toUnderstandingResponseResult } private StudySession findSession(Long sessionId) { - return curriculumRepository.findById(Math.toIntExact(sessionId)) + return curriculumRepository.findById(Math.toIntExact(sessionId.intValue())) .orElseThrow(() -> new QuestionException(HttpStatus.NOT_FOUND, "세션을 찾을 수 없습니다.")); }