diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..c3f502a --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# 디폴트 무시된 파일 +/shelf/ +/workspace.xml +# 에디터 기반 HTTP 클라이언트 요청 +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/Spring.iml b/.idea/Spring.iml new file mode 100644 index 0000000..d6ebd48 --- /dev/null +++ b/.idea/Spring.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..23e1696 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/copilot.data.migration.agent.xml b/.idea/copilot.data.migration.agent.xml new file mode 100644 index 0000000..4ea72a9 --- /dev/null +++ b/.idea/copilot.data.migration.agent.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/copilot.data.migration.ask.xml b/.idea/copilot.data.migration.ask.xml new file mode 100644 index 0000000..7ef04e2 --- /dev/null +++ b/.idea/copilot.data.migration.ask.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/copilot.data.migration.ask2agent.xml b/.idea/copilot.data.migration.ask2agent.xml new file mode 100644 index 0000000..1f2ea11 --- /dev/null +++ b/.idea/copilot.data.migration.ask2agent.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/copilot.data.migration.edit.xml b/.idea/copilot.data.migration.edit.xml new file mode 100644 index 0000000..8648f94 --- /dev/null +++ b/.idea/copilot.data.migration.edit.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml new file mode 100644 index 0000000..b3abbcd --- /dev/null +++ b/.idea/dataSources.xml @@ -0,0 +1,18 @@ + + + + + mysql.8 + true + com.mysql.cj.jdbc.Driver + jdbc:mysql://localhost:3306/market + + + + + + + $ProjectFileDir$ + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..1a324e0 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,16 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 0000000..fdc392f --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..1edb17c --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..374fdf8 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules/demo.main.iml b/.idea/modules/demo.main.iml new file mode 100644 index 0000000..8b055e7 --- /dev/null +++ b/.idea/modules/demo.main.iml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/sqldialects.xml b/.idea/sqldialects.xml new file mode 100644 index 0000000..dbac908 --- /dev/null +++ b/.idea/sqldialects.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml new file mode 100644 index 0000000..2b63946 --- /dev/null +++ b/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..7f2e21e --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/README.md b/README.md index ea5e249..2fcdc14 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ DKU UMC Spring Boot 스터디 | 이승준 | 정윤철 | 설원준 | 윤영주 | 고희연 | 고경수 | 구현모 | | :------: | :------: | :------: | :------: | :------: | :------: | :------: | -| [이승준](https://github.com/hilees) | [정윤철](https://github.com/Yoonchulchung) | [설원준]() | [윤영주]() | [고희연]() | [고경수]() | [구현모]() | +| [이승준](https://github.com/hilees) | [정윤철](https://github.com/Yoonchulchung) | [설원준]() | [윤영주](https://github.com/som141) | [고희연]() | [고경수]() | [구현모]() |
diff --git a/out/production/Spring/application.properties b/out/production/Spring/application.properties new file mode 100644 index 0000000..2109a44 --- /dev/null +++ b/out/production/Spring/application.properties @@ -0,0 +1 @@ +spring.application.name=demo diff --git a/out/production/Spring/com/example/demo/ddl/market.sql b/out/production/Spring/com/example/demo/ddl/market.sql new file mode 100644 index 0000000..39fec43 --- /dev/null +++ b/out/production/Spring/com/example/demo/ddl/market.sql @@ -0,0 +1,143 @@ +CREATE TABLE users ( + id BIGINT NOT NULL AUTO_INCREMENT COMMENT '유저 고유 식별자', + email VARCHAR(50) NOT NULL COMMENT '로그인 이메일(유니크)', + password VARCHAR(100) NOT NULL COMMENT '비밀번호 해시(예: bcrypt 60자)', + name VARCHAR(20) NOT NULL COMMENT '이름', + gender TINYINT NOT NULL COMMENT '0:남, 1:여, 2:기타 등', + birth DATE NOT NULL COMMENT '생년월일', + address VARCHAR(50) NOT NULL COMMENT '주소', + point INT NOT NULL DEFAULT 0 COMMENT '총 포인트', + PRIMARY KEY (id), + UNIQUE KEY UK_users_email (email) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='user_information'; + +-- 음식 카테고리 +CREATE TABLE food ( + id BIGINT NOT NULL AUTO_INCREMENT COMMENT '음식 카테고리 ID', + name VARCHAR(40) NOT NULL COMMENT '음식 카테고리 이름', + PRIMARY KEY (id), + UNIQUE KEY UK_food_name (name) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='푸드카테고리'; + +-- 선호 음식 (유저-음식) +CREATE TABLE favorite_food ( + id BIGINT NOT NULL AUTO_INCREMENT COMMENT '유저선호음식코드', + food_id BIGINT NOT NULL COMMENT '음식 카테고리 ID', + user_id BIGINT NOT NULL COMMENT '유저 ID', + PRIMARY KEY (id), + UNIQUE KEY UK_favorite_food_user_food (user_id, food_id), + KEY IDX_favorite_food_food (food_id), + CONSTRAINT FK_favorite_food_food + FOREIGN KEY (food_id) REFERENCES food (id) + ON UPDATE CASCADE ON DELETE RESTRICT, + CONSTRAINT FK_favorite_food_users + FOREIGN KEY (user_id) REFERENCES users (id) + ON UPDATE CASCADE ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='선호음식'; + +-- 가게 +CREATE TABLE foodmarket ( + id BIGINT NOT NULL AUTO_INCREMENT COMMENT '가게 id', + name VARCHAR(40) NOT NULL COMMENT '상호명(유니크는 아님)', + address VARCHAR(50) NOT NULL COMMENT '가게주소', + open BOOLEAN NOT NULL COMMENT '영업중 여부', + content VARCHAR(200) NOT NULL COMMENT '가게 소개', + PRIMARY KEY (id), + KEY IDX_foodmarket_name (name) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='푸드 마켓'; + +-- 미션(가게별) +CREATE TABLE mission ( + id BIGINT NOT NULL AUTO_INCREMENT COMMENT '고유 미션 ID', + market_id BIGINT NOT NULL COMMENT '가게 id', + content VARCHAR(100) NOT NULL COMMENT '미션 내용', + mission_point INT NOT NULL COMMENT '미션 포인트', + PRIMARY KEY (id), + KEY IDX_mission_market (market_id), + CONSTRAINT FK_mission_foodmarket + FOREIGN KEY (market_id) REFERENCES foodmarket (id) + ON UPDATE CASCADE ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='마켓과 유저 사이의 미션'; + +-- 유저가 받은 미션(신규 신청 등) +CREATE TABLE mission_user ( + id BIGINT NOT NULL AUTO_INCREMENT, + mission_id BIGINT NOT NULL COMMENT '미션 ID', + user_id BIGINT NOT NULL COMMENT '유저 ID', + content VARCHAR(200) NOT NULL COMMENT '새 미션에 대한 내용', + PRIMARY KEY (id), + UNIQUE KEY UK_mission_user (mission_id, user_id), + KEY IDX_mission_user_mission (mission_id), + KEY IDX_mission_user_user (user_id), + CONSTRAINT FK_mission_user_mission + FOREIGN KEY (mission_id) REFERENCES mission (id) + ON UPDATE CASCADE ON DELETE CASCADE, + CONSTRAINT FK_mission_user_users + FOREIGN KEY (user_id) REFERENCES users (id) + ON UPDATE CASCADE ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='신규 미션 배정 내역'; + +-- 미션 권한/배정 토큰(사장님 확인용 번호) +CREATE TABLE permission_mission ( + id BIGINT NOT NULL AUTO_INCREMENT COMMENT '유저 미션할당번호(제시용)', + user_id BIGINT NOT NULL COMMENT '유저 ID', + mission_id BIGINT NOT NULL COMMENT '미션 ID', + PRIMARY KEY (id), + UNIQUE KEY UK_permission_mission (user_id, mission_id), + KEY IDX_permission_mission_user (user_id), + KEY IDX_permission_mission_mission (mission_id), + CONSTRAINT FK_permission_mission_users + FOREIGN KEY (user_id) REFERENCES users (id) + ON UPDATE CASCADE ON DELETE CASCADE, + CONSTRAINT FK_permission_mission_mission + FOREIGN KEY (mission_id) REFERENCES mission (id) + ON UPDATE CASCADE ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='미션 권한 배정'; + +-- 리뷰 +CREATE TABLE review ( + id BIGINT NOT NULL AUTO_INCREMENT COMMENT '리뷰 ID', + user_id BIGINT NOT NULL COMMENT '유저 ID', + market_id BIGINT NOT NULL COMMENT '가게 ID', + content VARCHAR(100) NOT NULL COMMENT '리뷰 내용', + star DECIMAL(2,1) NOT NULL COMMENT '0.0~5.0', + PRIMARY KEY (id), + KEY IDX_review_user (user_id), + KEY IDX_review_market (market_id), + CONSTRAINT CHK_review_star CHECK (star >= 0.0 AND star <= 5.0), + CONSTRAINT FK_review_users + FOREIGN KEY (user_id) REFERENCES users (id) + ON UPDATE CASCADE ON DELETE CASCADE, + CONSTRAINT FK_review_foodmarket + FOREIGN KEY (market_id) REFERENCES foodmarket (id) + ON UPDATE CASCADE ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='마켓과 유저 사이의 리뷰'; + +-- 리뷰 알림 +CREATE TABLE review_alarm ( + id BIGINT NOT NULL AUTO_INCREMENT, + user_id BIGINT NOT NULL COMMENT '유저 ID', + foodmarket_id BIGINT NOT NULL COMMENT '가게 ID', + content VARCHAR(200) NOT NULL COMMENT '알람 내용(리뷰 관련)', + PRIMARY KEY (id), + KEY IDX_review_alarm_user (user_id), + KEY IDX_review_alarm_market (foodmarket_id), + CONSTRAINT FK_review_alarm_users + FOREIGN KEY (user_id) REFERENCES users (id) + ON UPDATE CASCADE ON DELETE CASCADE, + CONSTRAINT FK_review_alarm_foodmarket + FOREIGN KEY (foodmarket_id) REFERENCES foodmarket (id) + ON UPDATE CASCADE ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='리뷰 알림'; + +-- 리뷰 이미지 +CREATE TABLE review_image ( + id BIGINT NOT NULL AUTO_INCREMENT COMMENT '고유 이미지 ID', + review_id BIGINT NOT NULL COMMENT '리뷰 ID', + url VARCHAR(200) NOT NULL COMMENT '이미지 URL', + PRIMARY KEY (id), + KEY IDX_review_image_review (review_id), + CONSTRAINT FK_review_image_review + FOREIGN KEY (review_id) REFERENCES review (id) + ON UPDATE CASCADE ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='리뷰 이미지들'; \ No newline at end of file diff --git a/week0/ddl.sql b/week0/ddl.sql new file mode 100644 index 0000000..e2a8515 --- /dev/null +++ b/week0/ddl.sql @@ -0,0 +1,151 @@ + +CREATE TABLE favorate_food +( + id long NOT NULL COMMENT '유저선호음식코드', + food_id long NOT NULL COMMENT '음식 카테고리를 정하고 함', + user_id long NOT NULL COMMENT '유저 (id)db내에서의 불변고유식별자', + PRIMARY KEY (id) +) COMMENT '선호음식'; + +CREATE TABLE food +( + id long NOT NULL DEFAULT autoincrement COMMENT '음식 카테고리를 정하고 함', + name VARCHAR(40) NOT NULL COMMENT '음식카테고리 이름', + PRIMARY KEY (id) +) COMMENT '푸드카테고리'; + +CREATE TABLE foodmarket +( + id long NOT NULL DEFAULT autoincrement COMMENT '가게 id', + name VARCHAR(40) NOT NULL COMMENT '상호명(유니크는 아님)', + address VARCHAR(50) NOT NULL COMMENT '가게주소', + open BOOLEAN NOT NULL COMMENT '영업중이냐?', + content VARCHAR(200) NOT NULL COMMENT '가계 내용', + PRIMARY KEY (id) +); + +CREATE TABLE mission +( + id long NOT NULL DEFAULT autoincrement COMMENT '고유미션_id', + market_id long NOT NULL COMMENT '가게 id', + content VARCHAR(100) NOT NULL COMMENT '미션내용', + mission_point INTEGER NOT NULL COMMENT '미션포인트', + PRIMARY KEY (id) +) COMMENT '마켓과 유저 사이의 미션'; + +CREATE TABLE mission+user +( + id long NOT NULL DEFAULT autoincrement, + mission_id long NOT NULL COMMENT '고유미션_id', + user_id long NOT NULL COMMENT '유저 (id)db내에서의 불변고유식별자', + content VARCHAR NOT NULL COMMENT '새 미션에 대한 내용', + PRIMARY KEY (id) +) COMMENT '신규 미션 받아라 유저!'; + +CREATE TABLE permission_mission +( + id long NOT NULL DEFAULT autoincrement COMMENT '유저 미션할당번호(이거 사장님한테 보여주면 댐)', + user_id long NOT NULL COMMENT '유저 (id)db내에서의 불변고유식별자', + mission_id long NOT NULL COMMENT '고유미션_id', + PRIMARY KEY (id) +); + +CREATE TABLE review +( + id long NOT NULL DEFAULT autoincrement COMMENT '리뷰 고유(id)', + user_id long NOT NULL COMMENT '유저 (id)db내에서의 불변고유식별자', + market_id long NOT NULL COMMENT '가게 id', + content VARCHAR(100) NOT NULL COMMENT '리뷰내용', + star FLOAT NOT NULL COMMENT '0~5까지', + PRIMARY KEY (id) +) COMMENT '마켓과 유저사이의 리뷰'; + +CREATE TABLE review_alram +( + id long NOT NULL DEFAULT autoincrement, + user_id long NOT NULL COMMENT '가게 id', + foodmarket_id long NOT NULL COMMENT '유저 (id)db내에서의 불변고유식별자', + content VARCHAR NOT NULL COMMENT '알람내용(리뷰관련 내용)', + PRIMARY KEY (id) +) COMMENT '리뷰해주세용'; + +CREATE TABLE review_image +( + id long NOT NULL DEFAULT autoincrement COMMENT '고유이미지_id', + review_id long NOT NULL COMMENT '리뷰 고유(id)', + url VARCHAR(200) NOT NULL COMMENT '이미지 uri', + PRIMARY KEY (id) +) COMMENT '리뷰에 쓰이는 이미지들'; + +CREATE TABLE user +( + id long NOT NULL DEFAULT autoincrement COMMENT '유저 (id)db내에서의 불변고유식별자', + email VARCHAR(50) NOT NULL COMMENT '로그인할때 이메일(유니크 속성주기)', + password VARCHAR(30) NULL COMMENT '8자리이상 영문 숫자 특수문자 조합하셈', + name VARCHAR(20) NOT NULL COMMENT '이름임', + gender INTEGER NOT NULL COMMENT '남자0,여자1, 제삼의 성(?)3', + birth DATETIME NOT NULL COMMENT '8자리', + address VARCHAR(50) NOT NULL COMMENT '주소', + point INTEGER NOT NULL COMMENT '총 point', + PRIMARY KEY (id) +) COMMENT 'user_information'; + +ALTER TABLE review_image + ADD CONSTRAINT FK_review_TO_review_image + FOREIGN KEY (review_id) + REFERENCES review (id); + +ALTER TABLE permission_mission + ADD CONSTRAINT FK_user_TO_permission_mission + FOREIGN KEY (user_id) + REFERENCES user (id); + +ALTER TABLE review + ADD CONSTRAINT FK_foodmarket_TO_review + FOREIGN KEY (market_id) + REFERENCES foodmarket (id); + +ALTER TABLE review + ADD CONSTRAINT FK_user_TO_review + FOREIGN KEY (user_id) + REFERENCES user (id); + +ALTER TABLE mission + ADD CONSTRAINT FK_foodmarket_TO_mission + FOREIGN KEY (market_id) + REFERENCES foodmarket (id); + +ALTER TABLE permission_mission + ADD CONSTRAINT FK_mission_TO_permission_mission + FOREIGN KEY (mission_id) + REFERENCES mission (id); + +ALTER TABLE favorate_food + ADD CONSTRAINT FK_food_TO_favorate_food + FOREIGN KEY (food_id) + REFERENCES food (id); + +ALTER TABLE favorate_food + ADD CONSTRAINT FK_user_TO_favorate_food + FOREIGN KEY (user_id) + REFERENCES user (id); + +ALTER TABLE review_alram + ADD CONSTRAINT FK_foodmarket_TO_review_alram + FOREIGN KEY (user_id) + REFERENCES foodmarket (id); + +ALTER TABLE review_alram + ADD CONSTRAINT FK_user_TO_review_alram + FOREIGN KEY (foodmarket_id) + REFERENCES user (id); + +ALTER TABLE mission+user + ADD CONSTRAINT FK_mission_TO_mission+user + FOREIGN KEY (mission_id) + REFERENCES mission (id); + +ALTER TABLE mission+user + ADD CONSTRAINT FK_user_TO_mission+user + FOREIGN KEY (user_id) + REFERENCES user (id); diff --git a/week0/erd.vuerd.json b/week0/erd.vuerd.json new file mode 100644 index 0000000..0501b0f --- /dev/null +++ b/week0/erd.vuerd.json @@ -0,0 +1,2265 @@ +{ + "$schema": "https://raw.githubusercontent.com/dineug/erd-editor/main/json-schema/schema.json", + "version": "3.0.0", + "settings": { + "width": 2000, + "height": 2000, + "scrollTop": -201.8457, + "scrollLeft": -140.64, + "zoomLevel": 0.85, + "show": 431, + "database": 4, + "databaseName": "", + "canvasType": "@dineug/erd-editor/builtin-schema-sql", + "language": 1, + "tableNameCase": 4, + "columnNameCase": 2, + "bracketType": 1, + "relationshipDataTypeSync": true, + "relationshipOptimization": false, + "columnOrder": [ + 1, + 2, + 4, + 8, + 16, + 32, + 64 + ], + "maxWidthComment": -1, + "ignoreSaveSettings": 0 + }, + "doc": { + "tableIds": [ + "1h1BgNxEvKkk6gEY5CMPg", + "jAhMpDau6eAWbR8ifxIk7", + "U1KhhqX8xZco_6X76lUUF", + "eOdf64btd3jXZ3odam357", + "P7FiH9nZLSpro47ZFRs82", + "BrlSmoR0vOA5wp7DxG5we", + "0JvjupyrC_HXBhFN_shc4", + "spDJoG67LKxqns5k01Sw8", + "gb4iYsr3YyVUrKqBUXFw4", + "yoRJ0I0phZM05vvjpplpG" + ], + "relationshipIds": [ + "95LGlwc5ro-THdZsaS9E_", + "-CCfH3cvSxvdbUzVIjRqx", + "3NAwaUh04XxCu9WtiJZGQ", + "hsUPR9Uagl9UsCj3_v7-D", + "TaYFbfPGvec7SZetfjcbm", + "nrUSGKD4XjLLYG8gtNeX0", + "rRR3UlrXRccW3-FvusZwo", + "lF_M2fq7NZ-MM9bQ_QFa9", + "Zh4XQbVDVYrT3m77ypVoQ", + "28vE14zEXeDiQ9WZAlOgr", + "Fl9pEIK2AlrkViNySHsbU", + "75cGtrFxrlxv8h9HNbs_x" + ], + "indexIds": [], + "memoIds": [] + }, + "collections": { + "tableEntities": { + "XWGdk108BNIBTS4E5LyA-": { + "id": "XWGdk108BNIBTS4E5LyA-", + "name": "user", + "comment": "유저임", + "columnIds": [ + "HZU82lNKDOrgajhOG536V", + "t_RLBWhrZFUxlJ5bjRnfk", + "0kcH_8Z8tm3Ga1etxwa8U", + "9MBWULvb4IO29t17QfULg", + "4tkb0skf9LlAChFmM0d2q" + ], + "seqColumnIds": [ + "HZU82lNKDOrgajhOG536V", + "t_RLBWhrZFUxlJ5bjRnfk", + "0kcH_8Z8tm3Ga1etxwa8U", + "C4drmy-Oz1fH_pvwRy2ya", + "9MBWULvb4IO29t17QfULg", + "4tkb0skf9LlAChFmM0d2q" + ], + "ui": { + "x": 173, + "y": 262, + "zIndex": 102, + "widthName": 60, + "widthComment": 60, + "color": "" + }, + "meta": { + "updateAt": 1758383326408, + "createAt": 1758064771910 + } + }, + "1h1BgNxEvKkk6gEY5CMPg": { + "id": "1h1BgNxEvKkk6gEY5CMPg", + "name": "user", + "comment": "user_information", + "columnIds": [ + "Gh8Azk72nGvgic4QJ8XZ4", + "VT6dpeOGqyA1I7KQ6b4mK", + "R09EvHSVhLqVeSYQg8Dqa", + "sRdypptEf1G5rSsm6FX6g", + "k5QSZS8WtQ23EvpJsMuyy", + "4I1ptgrrjPfZ7kwaNL-gI", + "9YJk76nhsU2pQJHAgGuCQ", + "TdPHSn_WW5ipoJJugiMDr" + ], + "seqColumnIds": [ + "Gh8Azk72nGvgic4QJ8XZ4", + "VT6dpeOGqyA1I7KQ6b4mK", + "R09EvHSVhLqVeSYQg8Dqa", + "sRdypptEf1G5rSsm6FX6g", + "k5QSZS8WtQ23EvpJsMuyy", + "4I1ptgrrjPfZ7kwaNL-gI", + "9YJk76nhsU2pQJHAgGuCQ", + "TdPHSn_WW5ipoJJugiMDr" + ], + "ui": { + "x": 37.9412, + "y": 715.9412, + "zIndex": 2, + "widthName": 60, + "widthComment": 91, + "color": "" + }, + "meta": { + "updateAt": 1758522460041, + "createAt": 1758383331119 + } + }, + "jAhMpDau6eAWbR8ifxIk7": { + "id": "jAhMpDau6eAWbR8ifxIk7", + "name": "foodmarket", + "comment": "", + "columnIds": [ + "Zq1avcoEnaVsfbppogJ44", + "I-JEAuVy-GEHNuhd_R1Fl", + "sqis3p25lk_He3rwnCuDc", + "O0ry03x6NOpDvBTK0CMbb", + "cU-E1DpewiYVt3r5W3J67" + ], + "seqColumnIds": [ + "Zq1avcoEnaVsfbppogJ44", + "I-JEAuVy-GEHNuhd_R1Fl", + "sqis3p25lk_He3rwnCuDc", + "O0ry03x6NOpDvBTK0CMbb", + "frGgeCTDN_R-BO-pHTIZU", + "qQ4TPs1xEDtsaN2F69b8n", + "rWUlSO-Zx2_CzblYkScil", + "cU-E1DpewiYVt3r5W3J67", + "dUWHJDNXCbHc3eEVauegi" + ], + "ui": { + "x": 1469, + "y": 363, + "zIndex": 9, + "widthName": 64, + "widthComment": 60, + "color": "" + }, + "meta": { + "updateAt": 1758521924972, + "createAt": 1758383344116 + } + }, + "U1KhhqX8xZco_6X76lUUF": { + "id": "U1KhhqX8xZco_6X76lUUF", + "name": "review", + "comment": "마켓과 유저사이의 리뷰", + "columnIds": [ + "icCcVDsFAlzlq4FjE0ubK", + "r28FAr4NRktLF6jeinkQI", + "CVbY3U1IRbHYIv2_ABSi8", + "K2GSuWKKwoSYl0WoAcju0", + "TL3kiOfuBdtpAHnnAtq7x" + ], + "seqColumnIds": [ + "5rLifUB24BKsa7wxaS7UE", + "g3rYiPsqjN477uVH0qkLg", + "icCcVDsFAlzlq4FjE0ubK", + "r2POd-u5Q9w3HwIM7oZpS", + "VbAABiEWDtaW-BR5OX5e7", + "r28FAr4NRktLF6jeinkQI", + "CVbY3U1IRbHYIv2_ABSi8", + "K2GSuWKKwoSYl0WoAcju0", + "TL3kiOfuBdtpAHnnAtq7x" + ], + "ui": { + "x": 719, + "y": 363, + "zIndex": 25, + "widthName": 60, + "widthComment": 129, + "color": "" + }, + "meta": { + "updateAt": 1758521507399, + "createAt": 1758383404844 + } + }, + "eOdf64btd3jXZ3odam357": { + "id": "eOdf64btd3jXZ3odam357", + "name": "mission", + "comment": "마켓과 유저 사이의 미션", + "columnIds": [ + "ReQt_SxQicmPN6FInTteO", + "db5bXfLzqmyQYh_JjRPiy", + "bUJ9zMEJqs9SvY5IKsqc6", + "9WTFvYP-wRigq5OnVHaBt" + ], + "seqColumnIds": [ + "ReQt_SxQicmPN6FInTteO", + "db5bXfLzqmyQYh_JjRPiy", + "aI9MAW7Hiyy_vI2M7Pm0-", + "8rJpsU0PXxwff0XAgxqMm", + "SBNOyYuMRXvVKFCbagHDJ", + "bUJ9zMEJqs9SvY5IKsqc6", + "9WTFvYP-wRigq5OnVHaBt", + "ldych5z2WqoYn7KrDYRkC" + ], + "ui": { + "x": 1492, + "y": 832, + "zIndex": 29, + "widthName": 60, + "widthComment": 132, + "color": "" + }, + "meta": { + "updateAt": 1758522109141, + "createAt": 1758383411510 + } + }, + "fF0hgBzZeatjUrv0tbY6I": { + "id": "fF0hgBzZeatjUrv0tbY6I", + "name": "", + "comment": "", + "columnIds": [], + "seqColumnIds": [], + "ui": { + "x": 200, + "y": 110, + "zIndex": 291, + "widthName": 60, + "widthComment": 60, + "color": "" + }, + "meta": { + "updateAt": 1758385063879, + "createAt": 1758385063879 + } + }, + "P7FiH9nZLSpro47ZFRs82": { + "id": "P7FiH9nZLSpro47ZFRs82", + "name": "review_image", + "comment": "리뷰에 쓰이는 이미지들", + "columnIds": [ + "46mPj-jrSqP7EJVOWDkx6", + "TuNRq7Dmvw9HkKmJzmHyA", + "JpL2_wJdOTerJ5aBqveT3" + ], + "seqColumnIds": [ + "46mPj-jrSqP7EJVOWDkx6", + "TuNRq7Dmvw9HkKmJzmHyA", + "JpL2_wJdOTerJ5aBqveT3" + ], + "ui": { + "x": 778, + "y": 77, + "zIndex": 365, + "widthName": 74, + "widthComment": 129, + "color": "" + }, + "meta": { + "updateAt": 1758452632100, + "createAt": 1758385868614 + } + }, + "rg9wZvwotSRlkEuHNiT-a": { + "id": "rg9wZvwotSRlkEuHNiT-a", + "name": "a", + "comment": "", + "columnIds": [], + "seqColumnIds": [], + "ui": { + "x": 158, + "y": 145, + "zIndex": 447, + "widthName": 60, + "widthComment": 60, + "color": "" + }, + "meta": { + "updateAt": 1758453350856, + "createAt": 1758453328467 + } + }, + "GNTqlOzVMcjrNhTKrLaOf": { + "id": "GNTqlOzVMcjrNhTKrLaOf", + "name": "", + "comment": "", + "columnIds": [], + "seqColumnIds": [], + "ui": { + "x": 229, + "y": 771, + "zIndex": 448, + "widthName": 60, + "widthComment": 60, + "color": "" + }, + "meta": { + "updateAt": 1758471120574, + "createAt": 1758471113180 + } + }, + "XWcAkkKP3pemRFkafGh7s": { + "id": "XWcAkkKP3pemRFkafGh7s", + "name": "", + "comment": "", + "columnIds": [], + "seqColumnIds": [], + "ui": { + "x": 200, + "y": 102, + "zIndex": 374, + "widthName": 60, + "widthComment": 60, + "color": "" + }, + "meta": { + "updateAt": 1758520909487, + "createAt": 1758520909487 + } + }, + "BrlSmoR0vOA5wp7DxG5we": { + "id": "BrlSmoR0vOA5wp7DxG5we", + "name": "permission_mission", + "comment": "", + "columnIds": [ + "D831T8cqOSlCjjniUaDc2", + "Zl7soLlH7_f8dROOoajF6", + "7uVQuguLC9lssgHo5xhLG" + ], + "seqColumnIds": [ + "D831T8cqOSlCjjniUaDc2", + "Zl7soLlH7_f8dROOoajF6", + "CkI_d-LBnoNOjBGw3ay_0", + "7uVQuguLC9lssgHo5xhLG" + ], + "ui": { + "x": 693, + "y": 848, + "zIndex": 378, + "widthName": 105, + "widthComment": 60, + "color": "" + }, + "meta": { + "updateAt": 1758522022162, + "createAt": 1758520950080 + } + }, + "0JvjupyrC_HXBhFN_shc4": { + "id": "0JvjupyrC_HXBhFN_shc4", + "name": "review_alram", + "comment": "리뷰해주세용", + "columnIds": [ + "_bry5dwUtsSl6H9ydBcm1", + "bHXNv5MX53K6nr7N3Q8eK", + "xUscWzzz5Gz5EdHQRfk_N", + "zpzXrlmb7ooqirK9DmIXM" + ], + "seqColumnIds": [ + "_bry5dwUtsSl6H9ydBcm1", + "bHXNv5MX53K6nr7N3Q8eK", + "xUscWzzz5Gz5EdHQRfk_N", + "zpzXrlmb7ooqirK9DmIXM" + ], + "ui": { + "x": 766.2355, + "y": 592.1762, + "zIndex": 401, + "widthName": 71, + "widthComment": 74, + "color": "" + }, + "meta": { + "updateAt": 1758523203551, + "createAt": 1758521232356 + } + }, + "spDJoG67LKxqns5k01Sw8": { + "id": "spDJoG67LKxqns5k01Sw8", + "name": "food", + "comment": "푸드카테고리", + "columnIds": [ + "hxvxT750NkNHHEtYpUicL", + "s7jRm5AKMPwQqU88tFn6B" + ], + "seqColumnIds": [ + "hxvxT750NkNHHEtYpUicL", + "s7jRm5AKMPwQqU88tFn6B" + ], + "ui": { + "x": 79.9999, + "y": 209.2066, + "zIndex": 487, + "widthName": 60, + "widthComment": 74, + "color": "" + }, + "meta": { + "updateAt": 1758522508144, + "createAt": 1758522257974 + } + }, + "gb4iYsr3YyVUrKqBUXFw4": { + "id": "gb4iYsr3YyVUrKqBUXFw4", + "name": "favorate_food", + "comment": "선호음식", + "columnIds": [ + "tVkpMHG_0E5JLY_yJf3c9", + "MbXpR0Vw0Si2O1rM9QRbA", + "TgQ6AVIBficrVrE_W4Sk2" + ], + "seqColumnIds": [ + "tVkpMHG_0E5JLY_yJf3c9", + "MbXpR0Vw0Si2O1rM9QRbA", + "TgQ6AVIBficrVrE_W4Sk2" + ], + "ui": { + "x": 67.0591, + "y": 491.5598, + "zIndex": 530, + "widthName": 75, + "widthComment": 60, + "color": "" + }, + "meta": { + "updateAt": 1758522510966, + "createAt": 1758522378614 + } + }, + "yoRJ0I0phZM05vvjpplpG": { + "id": "yoRJ0I0phZM05vvjpplpG", + "name": "mission+user", + "comment": "신규 미션 받아라 유저!", + "columnIds": [ + "zVhoZPYp_b61QSqtMvvQq", + "qOaGuBBg_Wfz0JKwfaKUe", + "8U14FBzF_FqWWddLVhfeB", + "zJlYvdBeyNcIvgcKSBk4w" + ], + "seqColumnIds": [ + "zVhoZPYp_b61QSqtMvvQq", + "qOaGuBBg_Wfz0JKwfaKUe", + "8U14FBzF_FqWWddLVhfeB", + "zJlYvdBeyNcIvgcKSBk4w" + ], + "ui": { + "x": 721.1765, + "y": 1039.7949, + "zIndex": 581, + "widthName": 73, + "widthComment": 123, + "color": "" + }, + "meta": { + "updateAt": 1758528597424, + "createAt": 1758523047319 + } + }, + "rDtD8_KsF_UXR7P4hrBJt": { + "id": "rDtD8_KsF_UXR7P4hrBJt", + "name": "", + "comment": "", + "columnIds": [], + "seqColumnIds": [], + "ui": { + "x": 58.823529411764696, + "y": 185.67729411764702, + "zIndex": 590, + "widthName": 60, + "widthComment": 60, + "color": "" + }, + "meta": { + "updateAt": 1758523083601, + "createAt": 1758523083601 + } + } + }, + "tableColumnEntities": { + "HZU82lNKDOrgajhOG536V": { + "id": "HZU82lNKDOrgajhOG536V", + "tableId": "XWGdk108BNIBTS4E5LyA-", + "name": "id", + "comment": "고유식별자임", + "dataType": "INTEGER", + "default": "", + "options": 10, + "ui": { + "keys": 1, + "widthName": 60, + "widthComment": 74, + "widthDataType": 60, + "widthDefault": 60 + }, + "meta": { + "updateAt": 1758064848008, + "createAt": 1758064781776 + } + }, + "t_RLBWhrZFUxlJ5bjRnfk": { + "id": "t_RLBWhrZFUxlJ5bjRnfk", + "tableId": "XWGdk108BNIBTS4E5LyA-", + "name": "name", + "comment": "있어야함", + "dataType": "VARCHAR", + "default": "", + "options": 8, + "ui": { + "keys": 0, + "widthName": 60, + "widthComment": 60, + "widthDataType": 60, + "widthDefault": 60 + }, + "meta": { + "updateAt": 1758064876275, + "createAt": 1758064857525 + } + }, + "0kcH_8Z8tm3Ga1etxwa8U": { + "id": "0kcH_8Z8tm3Ga1etxwa8U", + "tableId": "XWGdk108BNIBTS4E5LyA-", + "name": "kakao", + "comment": "디폴트는 false", + "dataType": "BOOLEAN", + "default": "false", + "options": 8, + "ui": { + "keys": 0, + "widthName": 60, + "widthComment": 77, + "widthDataType": 60, + "widthDefault": 60 + }, + "meta": { + "updateAt": 1758064966134, + "createAt": 1758064876276 + } + }, + "C4drmy-Oz1fH_pvwRy2ya": { + "id": "C4drmy-Oz1fH_pvwRy2ya", + "tableId": "XWGdk108BNIBTS4E5LyA-", + "name": "", + "comment": "", + "dataType": "", + "default": "", + "options": 0, + "ui": { + "keys": 0, + "widthName": 60, + "widthComment": 60, + "widthDataType": 60, + "widthDefault": 60 + }, + "meta": { + "updateAt": 1758065019725, + "createAt": 1758065009459 + } + }, + "9MBWULvb4IO29t17QfULg": { + "id": "9MBWULvb4IO29t17QfULg", + "tableId": "XWGdk108BNIBTS4E5LyA-", + "name": "create_time", + "comment": "생성시간", + "dataType": "DATETIME(6)", + "default": "", + "options": 8, + "ui": { + "keys": 0, + "widthName": 63, + "widthComment": 60, + "widthDataType": 69, + "widthDefault": 60 + }, + "meta": { + "updateAt": 1758065275286, + "createAt": 1758065274645 + } + }, + "4tkb0skf9LlAChFmM0d2q": { + "id": "4tkb0skf9LlAChFmM0d2q", + "tableId": "XWGdk108BNIBTS4E5LyA-", + "name": "update_time", + "comment": "업데이트 타임", + "dataType": "DATETIME(6)", + "default": "", + "options": 8, + "ui": { + "keys": 0, + "widthName": 68, + "widthComment": 77, + "widthDataType": 69, + "widthDefault": 60 + }, + "meta": { + "updateAt": 1758065275286, + "createAt": 1758065275286 + } + }, + "Gh8Azk72nGvgic4QJ8XZ4": { + "id": "Gh8Azk72nGvgic4QJ8XZ4", + "tableId": "1h1BgNxEvKkk6gEY5CMPg", + "name": "id", + "comment": "유저 (id)db내에서의 불변고유식별자", + "dataType": "long", + "default": "autoincrement", + "options": 10, + "ui": { + "keys": 1, + "widthName": 60, + "widthComment": 196, + "widthDataType": 60, + "widthDefault": 79 + }, + "meta": { + "updateAt": 1758385536289, + "createAt": 1758383453193 + } + }, + "sRdypptEf1G5rSsm6FX6g": { + "id": "sRdypptEf1G5rSsm6FX6g", + "tableId": "1h1BgNxEvKkk6gEY5CMPg", + "name": "name", + "comment": "이름임", + "dataType": "VARCHAR(20)", + "default": "", + "options": 8, + "ui": { + "keys": 0, + "widthName": 60, + "widthComment": 60, + "widthDataType": 75, + "widthDefault": 60 + }, + "meta": { + "updateAt": 1758383677590, + "createAt": 1758383651007 + } + }, + "k5QSZS8WtQ23EvpJsMuyy": { + "id": "k5QSZS8WtQ23EvpJsMuyy", + "tableId": "1h1BgNxEvKkk6gEY5CMPg", + "name": "gender", + "comment": "남자0,여자1, 제삼의 성(?)3", + "dataType": "INTEGER", + "default": "", + "options": 8, + "ui": { + "keys": 0, + "widthName": 60, + "widthComment": 142, + "widthDataType": 60, + "widthDefault": 60 + }, + "meta": { + "updateAt": 1758384280668, + "createAt": 1758383677591 + } + }, + "4I1ptgrrjPfZ7kwaNL-gI": { + "id": "4I1ptgrrjPfZ7kwaNL-gI", + "tableId": "1h1BgNxEvKkk6gEY5CMPg", + "name": "birth", + "comment": "8자리", + "dataType": "DATETIME", + "default": "", + "options": 8, + "ui": { + "keys": 0, + "widthName": 60, + "widthComment": 60, + "widthDataType": 60, + "widthDefault": 60 + }, + "meta": { + "updateAt": 1758384114992, + "createAt": 1758383799994 + } + }, + "9YJk76nhsU2pQJHAgGuCQ": { + "id": "9YJk76nhsU2pQJHAgGuCQ", + "tableId": "1h1BgNxEvKkk6gEY5CMPg", + "name": "address", + "comment": "주소", + "dataType": "VARCHAR(50)", + "default": "", + "options": 8, + "ui": { + "keys": 0, + "widthName": 60, + "widthComment": 60, + "widthDataType": 75, + "widthDefault": 60 + }, + "meta": { + "updateAt": 1758384111863, + "createAt": 1758383858077 + } + }, + "VT6dpeOGqyA1I7KQ6b4mK": { + "id": "VT6dpeOGqyA1I7KQ6b4mK", + "tableId": "1h1BgNxEvKkk6gEY5CMPg", + "name": "email", + "comment": "로그인할때 이메일(유니크 속성주기)", + "dataType": "VARCHAR(50)", + "default": "", + "options": 8, + "ui": { + "keys": 0, + "widthName": 60, + "widthComment": 196, + "widthDataType": 75, + "widthDefault": 60 + }, + "meta": { + "updateAt": 1758384244390, + "createAt": 1758383923497 + } + }, + "R09EvHSVhLqVeSYQg8Dqa": { + "id": "R09EvHSVhLqVeSYQg8Dqa", + "tableId": "1h1BgNxEvKkk6gEY5CMPg", + "name": "password", + "comment": "8자리이상 영문 숫자 특수문자 조합하셈", + "dataType": "VARCHAR(30)", + "default": "", + "options": 0, + "ui": { + "keys": 0, + "widthName": 60, + "widthComment": 214, + "widthDataType": 75, + "widthDefault": 60 + }, + "meta": { + "updateAt": 1758384159973, + "createAt": 1758384128863 + } + }, + "Zq1avcoEnaVsfbppogJ44": { + "id": "Zq1avcoEnaVsfbppogJ44", + "tableId": "jAhMpDau6eAWbR8ifxIk7", + "name": "id", + "comment": "가게 id", + "dataType": "long", + "default": "autoincrement", + "options": 10, + "ui": { + "keys": 1, + "widthName": 60, + "widthComment": 60, + "widthDataType": 60, + "widthDefault": 79 + }, + "meta": { + "updateAt": 1758384854893, + "createAt": 1758384260781 + } + }, + "I-JEAuVy-GEHNuhd_R1Fl": { + "id": "I-JEAuVy-GEHNuhd_R1Fl", + "tableId": "jAhMpDau6eAWbR8ifxIk7", + "name": "name", + "comment": "상호명(유니크는 아님)", + "dataType": "VARCHAR(40)", + "default": "", + "options": 8, + "ui": { + "keys": 0, + "widthName": 60, + "widthComment": 121, + "widthDataType": 75, + "widthDefault": 60 + }, + "meta": { + "updateAt": 1758384675963, + "createAt": 1758384515354 + } + }, + "sqis3p25lk_He3rwnCuDc": { + "id": "sqis3p25lk_He3rwnCuDc", + "tableId": "jAhMpDau6eAWbR8ifxIk7", + "name": "address", + "comment": "가게주소", + "dataType": "VARCHAR(50)", + "default": "", + "options": 8, + "ui": { + "keys": 0, + "widthName": 60, + "widthComment": 60, + "widthDataType": 75, + "widthDefault": 60 + }, + "meta": { + "updateAt": 1758384684669, + "createAt": 1758384640113 + } + }, + "O0ry03x6NOpDvBTK0CMbb": { + "id": "O0ry03x6NOpDvBTK0CMbb", + "tableId": "jAhMpDau6eAWbR8ifxIk7", + "name": "open", + "comment": "영업중이냐?", + "dataType": "BOOLEAN", + "default": "", + "options": 8, + "ui": { + "keys": 0, + "widthName": 60, + "widthComment": 67, + "widthDataType": 60, + "widthDefault": 60 + }, + "meta": { + "updateAt": 1758384720579, + "createAt": 1758384684669 + } + }, + "frGgeCTDN_R-BO-pHTIZU": { + "id": "frGgeCTDN_R-BO-pHTIZU", + "tableId": "jAhMpDau6eAWbR8ifxIk7", + "name": "star", + "comment": "", + "dataType": "FLOAT", + "default": "0", + "options": 8, + "ui": { + "keys": 0, + "widthName": 60, + "widthComment": 60, + "widthDataType": 60, + "widthDefault": 60 + }, + "meta": { + "updateAt": 1758384751181, + "createAt": 1758384724228 + } + }, + "qQ4TPs1xEDtsaN2F69b8n": { + "id": "qQ4TPs1xEDtsaN2F69b8n", + "tableId": "jAhMpDau6eAWbR8ifxIk7", + "name": "mission", + "comment": "", + "dataType": "", + "default": "", + "options": 0, + "ui": { + "keys": 0, + "widthName": 60, + "widthComment": 60, + "widthDataType": 60, + "widthDefault": 60 + }, + "meta": { + "updateAt": 1758384908134, + "createAt": 1758384888313 + } + }, + "5rLifUB24BKsa7wxaS7UE": { + "id": "5rLifUB24BKsa7wxaS7UE", + "tableId": "U1KhhqX8xZco_6X76lUUF", + "name": "id", + "comment": "가게 id", + "dataType": "long", + "default": "autoincrement", + "options": 8, + "ui": { + "keys": 0, + "widthName": 60, + "widthComment": 60, + "widthDataType": 60, + "widthDefault": 79 + }, + "meta": { + "updateAt": 1758384978019, + "createAt": 1758384978019 + } + }, + "g3rYiPsqjN477uVH0qkLg": { + "id": "g3rYiPsqjN477uVH0qkLg", + "tableId": "U1KhhqX8xZco_6X76lUUF", + "name": "", + "comment": "", + "dataType": "", + "default": "", + "options": 10, + "ui": { + "keys": 1, + "widthName": 60, + "widthComment": 60, + "widthDataType": 60, + "widthDefault": 60 + }, + "meta": { + "updateAt": 1758384989514, + "createAt": 1758384989514 + } + }, + "rWUlSO-Zx2_CzblYkScil": { + "id": "rWUlSO-Zx2_CzblYkScil", + "tableId": "jAhMpDau6eAWbR8ifxIk7", + "name": "", + "comment": "", + "dataType": "", + "default": "", + "options": 8, + "ui": { + "keys": 0, + "widthName": 60, + "widthComment": 60, + "widthDataType": 60, + "widthDefault": 60 + }, + "meta": { + "updateAt": 1758384989952, + "createAt": 1758384989952 + } + }, + "r2POd-u5Q9w3HwIM7oZpS": { + "id": "r2POd-u5Q9w3HwIM7oZpS", + "tableId": "U1KhhqX8xZco_6X76lUUF", + "name": "market_id", + "comment": "가게 id", + "dataType": "long", + "default": "", + "options": 8, + "ui": { + "keys": 0, + "widthName": 60, + "widthComment": 60, + "widthDataType": 60, + "widthDefault": 60 + }, + "meta": { + "updateAt": 1758385507835, + "createAt": 1758385052865 + } + }, + "VbAABiEWDtaW-BR5OX5e7": { + "id": "VbAABiEWDtaW-BR5OX5e7", + "tableId": "U1KhhqX8xZco_6X76lUUF", + "name": "user_id", + "comment": "유저 (id)db내에서의 불변고유식별자", + "dataType": "long", + "default": "", + "options": 8, + "ui": { + "keys": 0, + "widthName": 60, + "widthComment": 196, + "widthDataType": 60, + "widthDefault": 60 + }, + "meta": { + "updateAt": 1758385488940, + "createAt": 1758385081817 + } + }, + "icCcVDsFAlzlq4FjE0ubK": { + "id": "icCcVDsFAlzlq4FjE0ubK", + "tableId": "U1KhhqX8xZco_6X76lUUF", + "name": "id", + "comment": "리뷰 고유(id)", + "dataType": "long", + "default": "autoincrement", + "options": 10, + "ui": { + "keys": 1, + "widthName": 60, + "widthComment": 71, + "widthDataType": 60, + "widthDefault": 79 + }, + "meta": { + "updateAt": 1758385549054, + "createAt": 1758385470718 + } + }, + "K2GSuWKKwoSYl0WoAcju0": { + "id": "K2GSuWKKwoSYl0WoAcju0", + "tableId": "U1KhhqX8xZco_6X76lUUF", + "name": "content", + "comment": "리뷰내용", + "dataType": "VARCHAR(100)", + "default": "", + "options": 8, + "ui": { + "keys": 0, + "widthName": 60, + "widthComment": 60, + "widthDataType": 81, + "widthDefault": 60 + }, + "meta": { + "updateAt": 1758385732751, + "createAt": 1758385622322 + } + }, + "TL3kiOfuBdtpAHnnAtq7x": { + "id": "TL3kiOfuBdtpAHnnAtq7x", + "tableId": "U1KhhqX8xZco_6X76lUUF", + "name": "star", + "comment": "0~5까지", + "dataType": "FLOAT", + "default": "", + "options": 8, + "ui": { + "keys": 0, + "widthName": 60, + "widthComment": 60, + "widthDataType": 60, + "widthDefault": 60 + }, + "meta": { + "updateAt": 1758386182875, + "createAt": 1758385737635 + } + }, + "46mPj-jrSqP7EJVOWDkx6": { + "id": "46mPj-jrSqP7EJVOWDkx6", + "tableId": "P7FiH9nZLSpro47ZFRs82", + "name": "id", + "comment": "고유이미지_id", + "dataType": "long", + "default": "autoincrement", + "options": 10, + "ui": { + "keys": 1, + "widthName": 60, + "widthComment": 77, + "widthDataType": 60, + "widthDefault": 79 + }, + "meta": { + "updateAt": 1758452601618, + "createAt": 1758385893212 + } + }, + "TuNRq7Dmvw9HkKmJzmHyA": { + "id": "TuNRq7Dmvw9HkKmJzmHyA", + "tableId": "P7FiH9nZLSpro47ZFRs82", + "name": "review_id", + "comment": "리뷰 고유(id)", + "dataType": "long", + "default": "", + "options": 8, + "ui": { + "keys": 2, + "widthName": 60, + "widthComment": 71, + "widthDataType": 60, + "widthDefault": 60 + }, + "meta": { + "updateAt": 1758386198028, + "createAt": 1758385949681 + } + }, + "cU-E1DpewiYVt3r5W3J67": { + "id": "cU-E1DpewiYVt3r5W3J67", + "tableId": "jAhMpDau6eAWbR8ifxIk7", + "name": "content", + "comment": "가계 내용", + "dataType": "VARCHAR(200)", + "default": "", + "options": 8, + "ui": { + "keys": 0, + "widthName": 60, + "widthComment": 60, + "widthDataType": 81, + "widthDefault": 60 + }, + "meta": { + "updateAt": 1758386124234, + "createAt": 1758386101996 + } + }, + "JpL2_wJdOTerJ5aBqveT3": { + "id": "JpL2_wJdOTerJ5aBqveT3", + "tableId": "P7FiH9nZLSpro47ZFRs82", + "name": "url", + "comment": "이미지 uri", + "dataType": "VARCHAR(200)", + "default": "", + "options": 8, + "ui": { + "keys": 0, + "widthName": 60, + "widthComment": 60, + "widthDataType": 81, + "widthDefault": 60 + }, + "meta": { + "updateAt": 1758386311629, + "createAt": 1758386284163 + } + }, + "aI9MAW7Hiyy_vI2M7Pm0-": { + "id": "aI9MAW7Hiyy_vI2M7Pm0-", + "tableId": "eOdf64btd3jXZ3odam357", + "name": "market_id", + "comment": "가게 id", + "dataType": "long", + "default": "", + "options": 8, + "ui": { + "keys": 0, + "widthName": 60, + "widthComment": 60, + "widthDataType": 60, + "widthDefault": 60 + }, + "meta": { + "updateAt": 1758452704017, + "createAt": 1758452663070 + } + }, + "8rJpsU0PXxwff0XAgxqMm": { + "id": "8rJpsU0PXxwff0XAgxqMm", + "tableId": "eOdf64btd3jXZ3odam357", + "name": "user_id", + "comment": "유저 (id)db내에서의 불변고유식별자", + "dataType": "long", + "default": "", + "options": 8, + "ui": { + "keys": 0, + "widthName": 60, + "widthComment": 196, + "widthDataType": 60, + "widthDefault": 60 + }, + "meta": { + "updateAt": 1758452706979, + "createAt": 1758452672201 + } + }, + "ReQt_SxQicmPN6FInTteO": { + "id": "ReQt_SxQicmPN6FInTteO", + "tableId": "eOdf64btd3jXZ3odam357", + "name": "id", + "comment": "고유미션_id", + "dataType": "long", + "default": "autoincrement", + "options": 10, + "ui": { + "keys": 1, + "widthName": 60, + "widthComment": 65, + "widthDataType": 60, + "widthDefault": 79 + }, + "meta": { + "updateAt": 1758452735615, + "createAt": 1758452682271 + } + }, + "SBNOyYuMRXvVKFCbagHDJ": { + "id": "SBNOyYuMRXvVKFCbagHDJ", + "tableId": "eOdf64btd3jXZ3odam357", + "name": "identifyed_number", + "comment": "고유 미션 번호(서로 확인 절차)", + "dataType": "long", + "default": "autoincrement", + "options": 8, + "ui": { + "keys": 0, + "widthName": 101, + "widthComment": 166, + "widthDataType": 60, + "widthDefault": 79 + }, + "meta": { + "updateAt": 1758452829093, + "createAt": 1758452765615 + } + }, + "TdPHSn_WW5ipoJJugiMDr": { + "id": "TdPHSn_WW5ipoJJugiMDr", + "tableId": "1h1BgNxEvKkk6gEY5CMPg", + "name": "point", + "comment": "총 point", + "dataType": "INTEGER", + "default": "", + "options": 8, + "ui": { + "keys": 0, + "widthName": 60, + "widthComment": 60, + "widthDataType": 60, + "widthDefault": 60 + }, + "meta": { + "updateAt": 1758453236942, + "createAt": 1758453214516 + } + }, + "bUJ9zMEJqs9SvY5IKsqc6": { + "id": "bUJ9zMEJqs9SvY5IKsqc6", + "tableId": "eOdf64btd3jXZ3odam357", + "name": "content", + "comment": "미션내용", + "dataType": "VARCHAR(100)", + "default": "", + "options": 8, + "ui": { + "keys": 0, + "widthName": 60, + "widthComment": 60, + "widthDataType": 81, + "widthDefault": 60 + }, + "meta": { + "updateAt": 1758453268496, + "createAt": 1758453244654 + } + }, + "9WTFvYP-wRigq5OnVHaBt": { + "id": "9WTFvYP-wRigq5OnVHaBt", + "tableId": "eOdf64btd3jXZ3odam357", + "name": "mission_point", + "comment": "미션포인트", + "dataType": "INTEGER", + "default": "", + "options": 8, + "ui": { + "keys": 0, + "widthName": 75, + "widthComment": 62, + "widthDataType": 60, + "widthDefault": 60 + }, + "meta": { + "updateAt": 1758453321214, + "createAt": 1758453268497 + } + }, + "Zl7soLlH7_f8dROOoajF6": { + "id": "Zl7soLlH7_f8dROOoajF6", + "tableId": "BrlSmoR0vOA5wp7DxG5we", + "name": "user_id", + "comment": "유저 (id)db내에서의 불변고유식별자", + "dataType": "long", + "default": "", + "options": 8, + "ui": { + "keys": 2, + "widthName": 60, + "widthComment": 196, + "widthDataType": 60, + "widthDefault": 60 + }, + "meta": { + "updateAt": 1758521084219, + "createAt": 1758521025058 + } + }, + "D831T8cqOSlCjjniUaDc2": { + "id": "D831T8cqOSlCjjniUaDc2", + "tableId": "BrlSmoR0vOA5wp7DxG5we", + "name": "id", + "comment": "유저 미션할당번호(이거 사장님한테 보여주면 댐)", + "dataType": "long", + "default": "autoincrement", + "options": 10, + "ui": { + "keys": 1, + "widthName": 60, + "widthComment": 262, + "widthDataType": 60, + "widthDefault": 79 + }, + "meta": { + "updateAt": 1758523290544, + "createAt": 1758521079088 + } + }, + "CkI_d-LBnoNOjBGw3ay_0": { + "id": "CkI_d-LBnoNOjBGw3ay_0", + "tableId": "BrlSmoR0vOA5wp7DxG5we", + "name": "mission_id", + "comment": "고유미션_id", + "dataType": "long", + "default": "", + "options": 8, + "ui": { + "keys": 0, + "widthName": 60, + "widthComment": 65, + "widthDataType": 60, + "widthDefault": 60 + }, + "meta": { + "updateAt": 1758521317800, + "createAt": 1758521244136 + } + }, + "dUWHJDNXCbHc3eEVauegi": { + "id": "dUWHJDNXCbHc3eEVauegi", + "tableId": "jAhMpDau6eAWbR8ifxIk7", + "name": "id", + "comment": "리뷰 고유(id)", + "dataType": "long", + "default": "autoincrement", + "options": 8, + "ui": { + "keys": 0, + "widthName": 60, + "widthComment": 71, + "widthDataType": 60, + "widthDefault": 79 + }, + "meta": { + "updateAt": 1758521399180, + "createAt": 1758521399180 + } + }, + "CVbY3U1IRbHYIv2_ABSi8": { + "id": "CVbY3U1IRbHYIv2_ABSi8", + "tableId": "U1KhhqX8xZco_6X76lUUF", + "name": "market_id", + "comment": "가게 id", + "dataType": "long", + "default": "", + "options": 8, + "ui": { + "keys": 2, + "widthName": 60, + "widthComment": 60, + "widthDataType": 60, + "widthDefault": 60 + }, + "meta": { + "updateAt": 1758521495607, + "createAt": 1758521413946 + } + }, + "r28FAr4NRktLF6jeinkQI": { + "id": "r28FAr4NRktLF6jeinkQI", + "tableId": "U1KhhqX8xZco_6X76lUUF", + "name": "user_id", + "comment": "유저 (id)db내에서의 불변고유식별자", + "dataType": "long", + "default": "", + "options": 8, + "ui": { + "keys": 2, + "widthName": 60, + "widthComment": 196, + "widthDataType": 60, + "widthDefault": 60 + }, + "meta": { + "updateAt": 1758521503092, + "createAt": 1758521432311 + } + }, + "db5bXfLzqmyQYh_JjRPiy": { + "id": "db5bXfLzqmyQYh_JjRPiy", + "tableId": "eOdf64btd3jXZ3odam357", + "name": "market_id", + "comment": "가게 id", + "dataType": "long", + "default": "", + "options": 8, + "ui": { + "keys": 2, + "widthName": 60, + "widthComment": 60, + "widthDataType": 60, + "widthDefault": 60 + }, + "meta": { + "updateAt": 1758522105219, + "createAt": 1758521673578 + } + }, + "7uVQuguLC9lssgHo5xhLG": { + "id": "7uVQuguLC9lssgHo5xhLG", + "tableId": "BrlSmoR0vOA5wp7DxG5we", + "name": "mission_id", + "comment": "고유미션_id", + "dataType": "long", + "default": "", + "options": 8, + "ui": { + "keys": 2, + "widthName": 60, + "widthComment": 65, + "widthDataType": 60, + "widthDefault": 60 + }, + "meta": { + "updateAt": 1758522009610, + "createAt": 1758521996327 + } + }, + "ldych5z2WqoYn7KrDYRkC": { + "id": "ldych5z2WqoYn7KrDYRkC", + "tableId": "eOdf64btd3jXZ3odam357", + "name": "point", + "comment": "", + "dataType": "INTEGER", + "default": "", + "options": 8, + "ui": { + "keys": 0, + "widthName": 60, + "widthComment": 60, + "widthDataType": 60, + "widthDefault": 60 + }, + "meta": { + "updateAt": 1758522098199, + "createAt": 1758522087155 + } + }, + "hxvxT750NkNHHEtYpUicL": { + "id": "hxvxT750NkNHHEtYpUicL", + "tableId": "spDJoG67LKxqns5k01Sw8", + "name": "id", + "comment": "음식 카테고리를 정하고 함", + "dataType": "long", + "default": "autoincrement", + "options": 10, + "ui": { + "keys": 1, + "widthName": 60, + "widthComment": 144, + "widthDataType": 60, + "widthDefault": 79 + }, + "meta": { + "updateAt": 1758522355077, + "createAt": 1758522293697 + } + }, + "s7jRm5AKMPwQqU88tFn6B": { + "id": "s7jRm5AKMPwQqU88tFn6B", + "tableId": "spDJoG67LKxqns5k01Sw8", + "name": "name", + "comment": "음식카테고리 이름", + "dataType": "VARCHAR(40)", + "default": "", + "options": 8, + "ui": { + "keys": 0, + "widthName": 60, + "widthComment": 101, + "widthDataType": 75, + "widthDefault": 60 + }, + "meta": { + "updateAt": 1758522377137, + "createAt": 1758522326861 + } + }, + "MbXpR0Vw0Si2O1rM9QRbA": { + "id": "MbXpR0Vw0Si2O1rM9QRbA", + "tableId": "gb4iYsr3YyVUrKqBUXFw4", + "name": "food_id", + "comment": "음식 카테고리를 정하고 함", + "dataType": "long", + "default": "", + "options": 8, + "ui": { + "keys": 2, + "widthName": 60, + "widthComment": 144, + "widthDataType": 60, + "widthDefault": 60 + }, + "meta": { + "updateAt": 1758522487576, + "createAt": 1758522454178 + } + }, + "TgQ6AVIBficrVrE_W4Sk2": { + "id": "TgQ6AVIBficrVrE_W4Sk2", + "tableId": "gb4iYsr3YyVUrKqBUXFw4", + "name": "user_id", + "comment": "유저 (id)db내에서의 불변고유식별자", + "dataType": "long", + "default": "", + "options": 8, + "ui": { + "keys": 2, + "widthName": 60, + "widthComment": 196, + "widthDataType": 60, + "widthDefault": 60 + }, + "meta": { + "updateAt": 1758522485448, + "createAt": 1758522460392 + } + }, + "tVkpMHG_0E5JLY_yJf3c9": { + "id": "tVkpMHG_0E5JLY_yJf3c9", + "tableId": "gb4iYsr3YyVUrKqBUXFw4", + "name": "id", + "comment": "유저선호음식코드", + "dataType": "long", + "default": "", + "options": 10, + "ui": { + "keys": 1, + "widthName": 60, + "widthComment": 98, + "widthDataType": 60, + "widthDefault": 60 + }, + "meta": { + "updateAt": 1758522500384, + "createAt": 1758522474856 + } + }, + "bHXNv5MX53K6nr7N3Q8eK": { + "id": "bHXNv5MX53K6nr7N3Q8eK", + "tableId": "0JvjupyrC_HXBhFN_shc4", + "name": "user_id", + "comment": "가게 id", + "dataType": "long", + "default": "", + "options": 8, + "ui": { + "keys": 2, + "widthName": 60, + "widthComment": 60, + "widthDataType": 60, + "widthDefault": 60 + }, + "meta": { + "updateAt": 1758523147397, + "createAt": 1758523031524 + } + }, + "xUscWzzz5Gz5EdHQRfk_N": { + "id": "xUscWzzz5Gz5EdHQRfk_N", + "tableId": "0JvjupyrC_HXBhFN_shc4", + "name": "foodmarket_id", + "comment": "유저 (id)db내에서의 불변고유식별자", + "dataType": "long", + "default": "", + "options": 8, + "ui": { + "keys": 2, + "widthName": 79, + "widthComment": 196, + "widthDataType": 60, + "widthDefault": 60 + }, + "meta": { + "updateAt": 1758523157435, + "createAt": 1758523036391 + } + }, + "zVhoZPYp_b61QSqtMvvQq": { + "id": "zVhoZPYp_b61QSqtMvvQq", + "tableId": "yoRJ0I0phZM05vvjpplpG", + "name": "id", + "comment": "", + "dataType": "long", + "default": "autoincrement", + "options": 10, + "ui": { + "keys": 1, + "widthName": 60, + "widthComment": 60, + "widthDataType": 60, + "widthDefault": 79 + }, + "meta": { + "updateAt": 1758523291783, + "createAt": 1758523066388 + } + }, + "qOaGuBBg_Wfz0JKwfaKUe": { + "id": "qOaGuBBg_Wfz0JKwfaKUe", + "tableId": "yoRJ0I0phZM05vvjpplpG", + "name": "mission_id", + "comment": "고유미션_id", + "dataType": "long", + "default": "", + "options": 8, + "ui": { + "keys": 2, + "widthName": 60, + "widthComment": 65, + "widthDataType": 60, + "widthDefault": 60 + }, + "meta": { + "updateAt": 1758523183284, + "createAt": 1758523081911 + } + }, + "8U14FBzF_FqWWddLVhfeB": { + "id": "8U14FBzF_FqWWddLVhfeB", + "tableId": "yoRJ0I0phZM05vvjpplpG", + "name": "user_id", + "comment": "유저 (id)db내에서의 불변고유식별자", + "dataType": "long", + "default": "", + "options": 8, + "ui": { + "keys": 2, + "widthName": 60, + "widthComment": 196, + "widthDataType": 60, + "widthDefault": 60 + }, + "meta": { + "updateAt": 1758523185505, + "createAt": 1758523093065 + } + }, + "_bry5dwUtsSl6H9ydBcm1": { + "id": "_bry5dwUtsSl6H9ydBcm1", + "tableId": "0JvjupyrC_HXBhFN_shc4", + "name": "id", + "comment": "", + "dataType": "long", + "default": "autoincrement", + "options": 10, + "ui": { + "keys": 1, + "widthName": 60, + "widthComment": 60, + "widthDataType": 60, + "widthDefault": 79 + }, + "meta": { + "updateAt": 1758523141535, + "createAt": 1758523099958 + } + }, + "zpzXrlmb7ooqirK9DmIXM": { + "id": "zpzXrlmb7ooqirK9DmIXM", + "tableId": "0JvjupyrC_HXBhFN_shc4", + "name": "content", + "comment": "알람내용(리뷰관련 내용)", + "dataType": "VARCHAR", + "default": "", + "options": 8, + "ui": { + "keys": 0, + "widthName": 60, + "widthComment": 133, + "widthDataType": 60, + "widthDefault": 60 + }, + "meta": { + "updateAt": 1758523238236, + "createAt": 1758523203551 + } + }, + "zJlYvdBeyNcIvgcKSBk4w": { + "id": "zJlYvdBeyNcIvgcKSBk4w", + "tableId": "yoRJ0I0phZM05vvjpplpG", + "name": "content", + "comment": "새 미션에 대한 내용", + "dataType": "VARCHAR", + "default": "", + "options": 8, + "ui": { + "keys": 0, + "widthName": 60, + "widthComment": 108, + "widthDataType": 60, + "widthDefault": 60 + }, + "meta": { + "updateAt": 1758523270003, + "createAt": 1758523247840 + } + } + }, + "relationshipEntities": { + "8Z53qVGZIj6OhsaZ9vGMf": { + "id": "8Z53qVGZIj6OhsaZ9vGMf", + "identification": false, + "relationshipType": 4, + "startRelationshipType": 2, + "start": { + "tableId": "jAhMpDau6eAWbR8ifxIk7", + "columnIds": [ + "Zq1avcoEnaVsfbppogJ44" + ], + "x": 1143, + "y": 242, + "direction": 1 + }, + "end": { + "tableId": "U1KhhqX8xZco_6X76lUUF", + "columnIds": [ + "5rLifUB24BKsa7wxaS7UE" + ], + "x": 1073, + "y": 166, + "direction": 2 + }, + "meta": { + "updateAt": 1758384978019, + "createAt": 1758384978019 + } + }, + "0WvFCow4O2GNAcz8b4Dql": { + "id": "0WvFCow4O2GNAcz8b4Dql", + "identification": false, + "relationshipType": 4, + "startRelationshipType": 2, + "start": { + "tableId": "U1KhhqX8xZco_6X76lUUF", + "columnIds": [ + "g3rYiPsqjN477uVH0qkLg" + ], + "x": 1073, + "y": 178, + "direction": 2 + }, + "end": { + "tableId": "jAhMpDau6eAWbR8ifxIk7", + "columnIds": [ + "rWUlSO-Zx2_CzblYkScil" + ], + "x": 1143, + "y": 254, + "direction": 1 + }, + "meta": { + "updateAt": 1758384989952, + "createAt": 1758384989952 + } + }, + "d8XK9y1W2Y2Sm6T5bJTST": { + "id": "d8XK9y1W2Y2Sm6T5bJTST", + "identification": false, + "relationshipType": 16, + "startRelationshipType": 2, + "start": { + "tableId": "jAhMpDau6eAWbR8ifxIk7", + "columnIds": [ + "Zq1avcoEnaVsfbppogJ44" + ], + "x": 1469, + "y": 462, + "direction": 1 + }, + "end": { + "tableId": "U1KhhqX8xZco_6X76lUUF", + "columnIds": [ + "r2POd-u5Q9w3HwIM7oZpS" + ], + "x": 1260, + "y": 451, + "direction": 2 + }, + "meta": { + "updateAt": 1758385052865, + "createAt": 1758385052865 + } + }, + "eiWfcJs5w-HFv5PEUBniY": { + "id": "eiWfcJs5w-HFv5PEUBniY", + "identification": false, + "relationshipType": 16, + "startRelationshipType": 2, + "start": { + "tableId": "1h1BgNxEvKkk6gEY5CMPg", + "columnIds": [ + "Gh8Azk72nGvgic4QJ8XZ4" + ], + "x": 620, + "y": 789, + "direction": 2 + }, + "end": { + "tableId": "U1KhhqX8xZco_6X76lUUF", + "columnIds": [ + "VbAABiEWDtaW-BR5OX5e7" + ], + "x": 719, + "y": 451, + "direction": 1 + }, + "meta": { + "updateAt": 1758385081817, + "createAt": 1758385081817 + } + }, + "95LGlwc5ro-THdZsaS9E_": { + "id": "95LGlwc5ro-THdZsaS9E_", + "identification": false, + "relationshipType": 4, + "startRelationshipType": 2, + "start": { + "tableId": "U1KhhqX8xZco_6X76lUUF", + "columnIds": [ + "icCcVDsFAlzlq4FjE0ubK" + ], + "x": 989.5, + "y": 363, + "direction": 4 + }, + "end": { + "tableId": "P7FiH9nZLSpro47ZFRs82", + "columnIds": [ + "TuNRq7Dmvw9HkKmJzmHyA" + ], + "x": 989, + "y": 205, + "direction": 8 + }, + "meta": { + "updateAt": 1758385949681, + "createAt": 1758385949681 + } + }, + "AhYoCwpMoUZjKuaTsV67y": { + "id": "AhYoCwpMoUZjKuaTsV67y", + "identification": false, + "relationshipType": 16, + "startRelationshipType": 2, + "start": { + "tableId": "jAhMpDau6eAWbR8ifxIk7", + "columnIds": [ + "Zq1avcoEnaVsfbppogJ44" + ], + "x": 1702, + "y": 563, + "direction": 8 + }, + "end": { + "tableId": "eOdf64btd3jXZ3odam357", + "columnIds": [ + "aI9MAW7Hiyy_vI2M7Pm0-" + ], + "x": 1745.5, + "y": 837, + "direction": 4 + }, + "meta": { + "updateAt": 1758452663071, + "createAt": 1758452663071 + } + }, + "bHRhHIOth5vgHKEWcIqCg": { + "id": "bHRhHIOth5vgHKEWcIqCg", + "identification": false, + "relationshipType": 16, + "startRelationshipType": 2, + "start": { + "tableId": "1h1BgNxEvKkk6gEY5CMPg", + "columnIds": [ + "Gh8Azk72nGvgic4QJ8XZ4" + ], + "x": 545, + "y": 526, + "direction": 2 + }, + "end": { + "tableId": "eOdf64btd3jXZ3odam357", + "columnIds": [ + "8rJpsU0PXxwff0XAgxqMm" + ], + "x": 742, + "y": 688, + "direction": 1 + }, + "meta": { + "updateAt": 1758452672201, + "createAt": 1758452672201 + } + }, + "-CCfH3cvSxvdbUzVIjRqx": { + "id": "-CCfH3cvSxvdbUzVIjRqx", + "identification": false, + "relationshipType": 4, + "startRelationshipType": 2, + "start": { + "tableId": "1h1BgNxEvKkk6gEY5CMPg", + "columnIds": [ + "Gh8Azk72nGvgic4QJ8XZ4" + ], + "x": 590.9412, + "y": 870.9412, + "direction": 2 + }, + "end": { + "tableId": "BrlSmoR0vOA5wp7DxG5we", + "columnIds": [ + "Zl7soLlH7_f8dROOoajF6" + ], + "x": 693, + "y": 912, + "direction": 1 + }, + "meta": { + "updateAt": 1758521025059, + "createAt": 1758521025059 + } + }, + "MBnlvlcsZqkaQ8PYk26m1": { + "id": "MBnlvlcsZqkaQ8PYk26m1", + "identification": false, + "relationshipType": 2, + "startRelationshipType": 2, + "start": { + "tableId": "eOdf64btd3jXZ3odam357", + "columnIds": [ + "ReQt_SxQicmPN6FInTteO" + ], + "x": 1533, + "y": 913, + "direction": 1 + }, + "end": { + "tableId": "BrlSmoR0vOA5wp7DxG5we", + "columnIds": [ + "CkI_d-LBnoNOjBGw3ay_0" + ], + "x": 1260, + "y": 912, + "direction": 2 + }, + "meta": { + "updateAt": 1758521244136, + "createAt": 1758521244136 + } + }, + "TZyzCxNXZSBj2H19VgZBh": { + "id": "TZyzCxNXZSBj2H19VgZBh", + "identification": false, + "relationshipType": 4, + "startRelationshipType": 2, + "start": { + "tableId": "U1KhhqX8xZco_6X76lUUF", + "columnIds": [ + "icCcVDsFAlzlq4FjE0ubK" + ], + "x": 1260, + "y": 451, + "direction": 2 + }, + "end": { + "tableId": "jAhMpDau6eAWbR8ifxIk7", + "columnIds": [ + "dUWHJDNXCbHc3eEVauegi" + ], + "x": 1469, + "y": 474, + "direction": 1 + }, + "meta": { + "updateAt": 1758521399180, + "createAt": 1758521399180 + } + }, + "3NAwaUh04XxCu9WtiJZGQ": { + "id": "3NAwaUh04XxCu9WtiJZGQ", + "identification": false, + "relationshipType": 4, + "startRelationshipType": 2, + "start": { + "tableId": "jAhMpDau6eAWbR8ifxIk7", + "columnIds": [ + "Zq1avcoEnaVsfbppogJ44" + ], + "x": 1469, + "y": 407, + "direction": 1 + }, + "end": { + "tableId": "U1KhhqX8xZco_6X76lUUF", + "columnIds": [ + "CVbY3U1IRbHYIv2_ABSi8" + ], + "x": 1260, + "y": 451, + "direction": 2 + }, + "meta": { + "updateAt": 1758521413946, + "createAt": 1758521413946 + } + }, + "hsUPR9Uagl9UsCj3_v7-D": { + "id": "hsUPR9Uagl9UsCj3_v7-D", + "identification": false, + "relationshipType": 4, + "startRelationshipType": 2, + "start": { + "tableId": "1h1BgNxEvKkk6gEY5CMPg", + "columnIds": [ + "Gh8Azk72nGvgic4QJ8XZ4" + ], + "x": 590.9412, + "y": 746.9412, + "direction": 2 + }, + "end": { + "tableId": "U1KhhqX8xZco_6X76lUUF", + "columnIds": [ + "r28FAr4NRktLF6jeinkQI" + ], + "x": 719, + "y": 451, + "direction": 1 + }, + "meta": { + "updateAt": 1758521432311, + "createAt": 1758521432311 + } + }, + "TaYFbfPGvec7SZetfjcbm": { + "id": "TaYFbfPGvec7SZetfjcbm", + "identification": false, + "relationshipType": 4, + "startRelationshipType": 2, + "start": { + "tableId": "jAhMpDau6eAWbR8ifxIk7", + "columnIds": [ + "Zq1avcoEnaVsfbppogJ44" + ], + "x": 1702, + "y": 539, + "direction": 8 + }, + "end": { + "tableId": "eOdf64btd3jXZ3odam357", + "columnIds": [ + "db5bXfLzqmyQYh_JjRPiy" + ], + "x": 1704.5, + "y": 832, + "direction": 4 + }, + "meta": { + "updateAt": 1758521673578, + "createAt": 1758521673578 + } + }, + "nrUSGKD4XjLLYG8gtNeX0": { + "id": "nrUSGKD4XjLLYG8gtNeX0", + "identification": false, + "relationshipType": 4, + "startRelationshipType": 2, + "start": { + "tableId": "eOdf64btd3jXZ3odam357", + "columnIds": [ + "ReQt_SxQicmPN6FInTteO" + ], + "x": 1492, + "y": 870, + "direction": 1 + }, + "end": { + "tableId": "BrlSmoR0vOA5wp7DxG5we", + "columnIds": [ + "7uVQuguLC9lssgHo5xhLG" + ], + "x": 1279, + "y": 912, + "direction": 2 + }, + "meta": { + "updateAt": 1758521996327, + "createAt": 1758521996327 + } + }, + "rRR3UlrXRccW3-FvusZwo": { + "id": "rRR3UlrXRccW3-FvusZwo", + "identification": false, + "relationshipType": 4, + "startRelationshipType": 2, + "start": { + "tableId": "spDJoG67LKxqns5k01Sw8", + "columnIds": [ + "hxvxT750NkNHHEtYpUicL" + ], + "x": 321.4999, + "y": 313.2066, + "direction": 8 + }, + "end": { + "tableId": "gb4iYsr3YyVUrKqBUXFw4", + "columnIds": [ + "MbXpR0Vw0Si2O1rM9QRbA" + ], + "x": 317.5591, + "y": 491.5598, + "direction": 4 + }, + "meta": { + "updateAt": 1758522454179, + "createAt": 1758522454179 + } + }, + "lF_M2fq7NZ-MM9bQ_QFa9": { + "id": "lF_M2fq7NZ-MM9bQ_QFa9", + "identification": false, + "relationshipType": 4, + "startRelationshipType": 2, + "start": { + "tableId": "1h1BgNxEvKkk6gEY5CMPg", + "columnIds": [ + "Gh8Azk72nGvgic4QJ8XZ4" + ], + "x": 314.4412, + "y": 715.9412, + "direction": 4 + }, + "end": { + "tableId": "gb4iYsr3YyVUrKqBUXFw4", + "columnIds": [ + "TgQ6AVIBficrVrE_W4Sk2" + ], + "x": 317.5591, + "y": 619.5598, + "direction": 8 + }, + "meta": { + "updateAt": 1758522460392, + "createAt": 1758522460392 + } + }, + "Zh4XQbVDVYrT3m77ypVoQ": { + "id": "Zh4XQbVDVYrT3m77ypVoQ", + "identification": false, + "relationshipType": 4, + "startRelationshipType": 2, + "start": { + "tableId": "jAhMpDau6eAWbR8ifxIk7", + "columnIds": [ + "Zq1avcoEnaVsfbppogJ44" + ], + "x": 1469, + "y": 495, + "direction": 1 + }, + "end": { + "tableId": "0JvjupyrC_HXBhFN_shc4", + "columnIds": [ + "bHXNv5MX53K6nr7N3Q8eK" + ], + "x": 1305.2355, + "y": 668.1762, + "direction": 2 + }, + "meta": { + "updateAt": 1758523031524, + "createAt": 1758523031524 + } + }, + "28vE14zEXeDiQ9WZAlOgr": { + "id": "28vE14zEXeDiQ9WZAlOgr", + "identification": false, + "relationshipType": 4, + "startRelationshipType": 2, + "start": { + "tableId": "1h1BgNxEvKkk6gEY5CMPg", + "columnIds": [ + "Gh8Azk72nGvgic4QJ8XZ4" + ], + "x": 590.9412, + "y": 808.9412, + "direction": 2 + }, + "end": { + "tableId": "0JvjupyrC_HXBhFN_shc4", + "columnIds": [ + "xUscWzzz5Gz5EdHQRfk_N" + ], + "x": 766.2355, + "y": 668.1762, + "direction": 1 + }, + "meta": { + "updateAt": 1758523036392, + "createAt": 1758523036392 + } + }, + "Fl9pEIK2AlrkViNySHsbU": { + "id": "Fl9pEIK2AlrkViNySHsbU", + "identification": false, + "relationshipType": 4, + "startRelationshipType": 2, + "start": { + "tableId": "eOdf64btd3jXZ3odam357", + "columnIds": [ + "ReQt_SxQicmPN6FInTteO" + ], + "x": 1492, + "y": 946, + "direction": 1 + }, + "end": { + "tableId": "yoRJ0I0phZM05vvjpplpG", + "columnIds": [ + "qOaGuBBg_Wfz0JKwfaKUe" + ], + "x": 1241.1765, + "y": 1115.7949, + "direction": 2 + }, + "meta": { + "updateAt": 1758523081911, + "createAt": 1758523081911 + } + }, + "75cGtrFxrlxv8h9HNbs_x": { + "id": "75cGtrFxrlxv8h9HNbs_x", + "identification": false, + "relationshipType": 4, + "startRelationshipType": 2, + "start": { + "tableId": "1h1BgNxEvKkk6gEY5CMPg", + "columnIds": [ + "Gh8Azk72nGvgic4QJ8XZ4" + ], + "x": 590.9412, + "y": 932.9412, + "direction": 2 + }, + "end": { + "tableId": "yoRJ0I0phZM05vvjpplpG", + "columnIds": [ + "8U14FBzF_FqWWddLVhfeB" + ], + "x": 721.1765, + "y": 1115.7949, + "direction": 1 + }, + "meta": { + "updateAt": 1758523093065, + "createAt": 1758523093065 + } + } + }, + "indexEntities": {}, + "indexColumnEntities": {}, + "memoEntities": {} + } +} \ No newline at end of file diff --git a/week1/ex1.sql b/week1/ex1.sql new file mode 100644 index 0000000..f0eff4f --- /dev/null +++ b/week1/ex1.sql @@ -0,0 +1,20 @@ +select * from review where review.market_id=”점포id”; +select (유저란에 있는 관련정보)from user where=(여기에 로그인 인증때 쓴 id값을 넣어줘야함) +select (선택할 정보)from permission_mission as p inner join mission as m on p.mission_id=m.id inner join food_market as f on m.market_id=f.id where p.user_id=”유저아이디” +limit 10 : 20 (대충 한페이지에 10개씩 담아서 조회하고 지금은 3페이지임; +홈 화면 쿼리(현재 선택 된 지역에서 도전이 가능한 미션 목록, 페이징 포함) 만약에 foodmarket테이블에 위치정보가 있다면 (거리 계산은 단순 차로 계산함) +select(정보들) from food_market as f join mission as m on f.id= m.mission_id +where foodmarket.distance<3 + +limit 10 : 20 (대충 한페이지에 10개씩 담아서 조회하고 지금은 3페이지임 +select (선택할 정보) +from permission_mission as p +inner join mission as m on p.mission_id=m.id +inner join food_market as f on m.market_id=f.id +where p.user_id=”유저아이디” +AND ( + points < :p + OR (m .points = :p AND m.created_at < :t) + OR (m.points = :p AND m.created_at = :t AND m.id < :m.id) + ) +order by m.point m.created_at diff --git a/week1/workbook.txt b/week1/workbook.txt new file mode 100644 index 0000000..ed8baf8 --- /dev/null +++ b/week1/workbook.txt @@ -0,0 +1,25 @@ +- [ ] 다양한 트랜젝션 상태와, 트랜젝션 전파에 대해 조사해주세요! + - acid의 i의 상태는 + + + + ![image.png](attachment:d296fec7-dfa8-47ad-9d3d-79fb98c3253f:image.png) + + +이렇게 되고 serializable로 갈수록 격리성이 높아져서 공유 데이터가 안전해 집니다. + +- [ ] 함수 기반 인덱스와 복합 인덱스에 대해 조사하고, +성능상 이점과 단점을 적어주세요! + - 인덱스는 보통 select 시에 데이터를 빠르게 찾기위함이 주 목적인데 이때 찾는 조건 where절을 잘 볼 필요가 있다 우리가 where절의 조건을 기본키로 했으면 큰 문제가 없겠지만 만약 함수식조건, 복합컬럼으로 조회했을때는 문제가 되기에 이를 해결하기 위한것이 + - 함수기반 인덱스와 복합 인덱스이다. + - 함수기반 인덱스는 abs(a,b)자체 계산값들을 인덱스로 저장하고 복합 인덱스는 CREATE INDEX idx_user_name_age ON users(name, age); 처럼 키를 묶어서 저장해 놓는다. + +함수기반 장단점 + +장점: where절에서 함수기반 서칭을 해도 빠르게 찾기가능. + +단점: 계산하고 저장해서 저장공간을 잡아먹는다. + +장점 : 복합인덱스: 복합키로 찾을때 빠른 서칭가능. + +단점 : 만약 히트 못시키면 그냥 저장공간 낭비 \ No newline at end of file diff --git a/week4/demo/.gitattributes b/week4/demo/.gitattributes new file mode 100644 index 0000000..8af972c --- /dev/null +++ b/week4/demo/.gitattributes @@ -0,0 +1,3 @@ +/gradlew text eol=lf +*.bat text eol=crlf +*.jar binary diff --git a/week4/demo/.gitignore b/week4/demo/.gitignore new file mode 100644 index 0000000..c2065bc --- /dev/null +++ b/week4/demo/.gitignore @@ -0,0 +1,37 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ diff --git a/week4/demo/build.gradle b/week4/demo/build.gradle new file mode 100644 index 0000000..a245330 --- /dev/null +++ b/week4/demo/build.gradle @@ -0,0 +1,66 @@ +plugins { + id 'java' + id 'org.springframework.boot' version '3.5.6' + id 'io.spring.dependency-management' version '1.1.7' +} + +group = 'study' +version = '0.0.1-SNAPSHOT' +description = 'demo' + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } +} + +configurations { + compileOnly { + extendsFrom annotationProcessor + } +} + +repositories { + mavenCentral() +} + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter-web' + compileOnly 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + runtimeOnly 'com.mysql:mysql-connector-j' + + // QueryDSL : OpenFeign + implementation "io.github.openfeign.querydsl:querydsl-jpa:7.0" + implementation "io.github.openfeign.querydsl:querydsl-core:7.0" + annotationProcessor "io.github.openfeign.querydsl:querydsl-apt:7.0:jpa" + annotationProcessor "jakarta.persistence:jakarta.persistence-api" + annotationProcessor "jakarta.annotation:jakarta.annotation-api" + // Swagger + implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.13' + implementation 'org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.13' + +} + +tasks.named('test') { + useJUnitPlatform() +} +def querydslDir = layout.buildDirectory.dir("generated/querydsl").get().asFile + +// 소스 세트에 생성 경로 추가 (구체적인 경로 지정) +sourceSets { + main.java.srcDirs += [ querydslDir ] +} + +// 컴파일 시 생성 경로 지정 +tasks.withType(JavaCompile).configureEach { + options.generatedSourceOutputDirectory.set(querydslDir) +} + +// clean 태스크에 생성 폴더 삭제 로직 추가 +clean.doLast { + file(querydslDir).deleteDir() +} \ No newline at end of file diff --git a/week4/demo/gradle/wrapper/gradle-wrapper.jar b/week4/demo/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..1b33c55 Binary files /dev/null and b/week4/demo/gradle/wrapper/gradle-wrapper.jar differ diff --git a/week4/demo/gradle/wrapper/gradle-wrapper.properties b/week4/demo/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..d4081da --- /dev/null +++ b/week4/demo/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/week4/demo/gradlew b/week4/demo/gradlew new file mode 100644 index 0000000..23d15a9 --- /dev/null +++ b/week4/demo/gradlew @@ -0,0 +1,251 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH="\\\"\\\"" + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/week4/demo/gradlew.bat b/week4/demo/gradlew.bat new file mode 100644 index 0000000..db3a6ac --- /dev/null +++ b/week4/demo/gradlew.bat @@ -0,0 +1,94 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH= + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/week4/demo/settings.gradle b/week4/demo/settings.gradle new file mode 100644 index 0000000..0a383dd --- /dev/null +++ b/week4/demo/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'demo' diff --git a/week4/demo/src/main/java/com/example/demo/DemoApplication.java b/week4/demo/src/main/java/com/example/demo/DemoApplication.java new file mode 100644 index 0000000..773eff3 --- /dev/null +++ b/week4/demo/src/main/java/com/example/demo/DemoApplication.java @@ -0,0 +1,15 @@ +package com.example.demo; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; + +@EnableJpaAuditing +@SpringBootApplication +public class DemoApplication { + + public static void main(String[] args) { + SpringApplication.run(DemoApplication.class, args); + } + +} diff --git a/week4/demo/src/main/java/com/example/demo/ddl/market.sql b/week4/demo/src/main/java/com/example/demo/ddl/market.sql new file mode 100644 index 0000000..9de3b7f --- /dev/null +++ b/week4/demo/src/main/java/com/example/demo/ddl/market.sql @@ -0,0 +1,144 @@ +use market; +CREATE TABLE users ( + id BIGINT NOT NULL AUTO_INCREMENT COMMENT '유저 고유 식별자', + email VARCHAR(50) NOT NULL COMMENT '로그인 이메일(유니크)', + password VARCHAR(100) NOT NULL COMMENT '비밀번호 해시(예: bcrypt 60자)', + name VARCHAR(20) NOT NULL COMMENT '이름', + gender TINYINT NOT NULL COMMENT '0:남, 1:여, 2:기타 등', + birth DATE NOT NULL COMMENT '생년월일', + address VARCHAR(50) NOT NULL COMMENT '주소', + point INT NOT NULL DEFAULT 0 COMMENT '총 포인트', + PRIMARY KEY (id), + UNIQUE KEY UK_users_email (email) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='user_information'; + +-- 음식 카테고리 +CREATE TABLE food ( + id BIGINT NOT NULL AUTO_INCREMENT COMMENT '음식 카테고리 ID', + name VARCHAR(40) NOT NULL COMMENT '음식 카테고리 이름', + PRIMARY KEY (id), + UNIQUE KEY UK_food_name (name) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='푸드카테고리'; + +-- 선호 음식 (유저-음식) +CREATE TABLE favorite_food ( + id BIGINT NOT NULL AUTO_INCREMENT COMMENT '유저선호음식코드', + food_id BIGINT NOT NULL COMMENT '음식 카테고리 ID', + user_id BIGINT NOT NULL COMMENT '유저 ID', + PRIMARY KEY (id), + UNIQUE KEY UK_favorite_food_user_food (user_id, food_id), + KEY IDX_favorite_food_food (food_id), + CONSTRAINT FK_favorite_food_food + FOREIGN KEY (food_id) REFERENCES food (id) + ON UPDATE CASCADE ON DELETE RESTRICT, + CONSTRAINT FK_favorite_food_users + FOREIGN KEY (user_id) REFERENCES users (id) + ON UPDATE CASCADE ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='선호음식'; + +-- 가게 +CREATE TABLE foodmarket ( + id BIGINT NOT NULL AUTO_INCREMENT COMMENT '가게 id', + name VARCHAR(40) NOT NULL COMMENT '상호명(유니크는 아님)', + address VARCHAR(50) NOT NULL COMMENT '가게주소', + open BOOLEAN NOT NULL COMMENT '영업중 여부', + content VARCHAR(200) NOT NULL COMMENT '가게 소개', + PRIMARY KEY (id), + KEY IDX_foodmarket_name (name) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='푸드 마켓'; + +-- 미션(가게별) +CREATE TABLE mission ( + id BIGINT NOT NULL AUTO_INCREMENT COMMENT '고유 미션 ID', + market_id BIGINT NOT NULL COMMENT '가게 id', + content VARCHAR(100) NOT NULL COMMENT '미션 내용', + mission_point INT NOT NULL COMMENT '미션 포인트', + PRIMARY KEY (id), + KEY IDX_mission_market (market_id), + CONSTRAINT FK_mission_foodmarket + FOREIGN KEY (market_id) REFERENCES foodmarket (id) + ON UPDATE CASCADE ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='마켓과 유저 사이의 미션'; + +-- 유저가 받은 미션(신규 신청 등) +CREATE TABLE mission_user ( + id BIGINT NOT NULL AUTO_INCREMENT, + mission_id BIGINT NOT NULL COMMENT '미션 ID', + user_id BIGINT NOT NULL COMMENT '유저 ID', + content VARCHAR(200) NOT NULL COMMENT '새 미션에 대한 내용', + PRIMARY KEY (id), + UNIQUE KEY UK_mission_user (mission_id, user_id), + KEY IDX_mission_user_mission (mission_id), + KEY IDX_mission_user_user (user_id), + CONSTRAINT FK_mission_user_mission + FOREIGN KEY (mission_id) REFERENCES mission (id) + ON UPDATE CASCADE ON DELETE CASCADE, + CONSTRAINT FK_mission_user_users + FOREIGN KEY (user_id) REFERENCES users (id) + ON UPDATE CASCADE ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='신규 미션 배정 내역'; + +-- 미션 권한/배정 토큰(사장님 확인용 번호) +CREATE TABLE permission_mission ( + id BIGINT NOT NULL AUTO_INCREMENT COMMENT '유저 미션할당번호(제시용)', + user_id BIGINT NOT NULL COMMENT '유저 ID', + mission_id BIGINT NOT NULL COMMENT '미션 ID', + PRIMARY KEY (id), + UNIQUE KEY UK_permission_mission (user_id, mission_id), + KEY IDX_permission_mission_user (user_id), + KEY IDX_permission_mission_mission (mission_id), + CONSTRAINT FK_permission_mission_users + FOREIGN KEY (user_id) REFERENCES users (id) + ON UPDATE CASCADE ON DELETE CASCADE, + CONSTRAINT FK_permission_mission_mission + FOREIGN KEY (mission_id) REFERENCES mission (id) + ON UPDATE CASCADE ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='미션 권한 배정'; + +-- 리뷰 +CREATE TABLE review ( + id BIGINT NOT NULL AUTO_INCREMENT COMMENT '리뷰 ID', + user_id BIGINT NOT NULL COMMENT '유저 ID', + market_id BIGINT NOT NULL COMMENT '가게 ID', + content VARCHAR(100) NOT NULL COMMENT '리뷰 내용', + star DECIMAL(2,1) NOT NULL COMMENT '0.0~5.0', + PRIMARY KEY (id), + KEY IDX_review_user (user_id), + KEY IDX_review_market (market_id), + CONSTRAINT CHK_review_star CHECK (star >= 0.0 AND star <= 5.0), + CONSTRAINT FK_review_users + FOREIGN KEY (user_id) REFERENCES users (id) + ON UPDATE CASCADE ON DELETE CASCADE, + CONSTRAINT FK_review_foodmarket + FOREIGN KEY (market_id) REFERENCES foodmarket (id) + ON UPDATE CASCADE ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='마켓과 유저 사이의 리뷰'; + +-- 리뷰 알림 +CREATE TABLE review_alarm ( + id BIGINT NOT NULL AUTO_INCREMENT, + user_id BIGINT NOT NULL COMMENT '유저 ID', + foodmarket_id BIGINT NOT NULL COMMENT '가게 ID', + content VARCHAR(200) NOT NULL COMMENT '알람 내용(리뷰 관련)', + PRIMARY KEY (id), + KEY IDX_review_alarm_user (user_id), + KEY IDX_review_alarm_market (foodmarket_id), + CONSTRAINT FK_review_alarm_users + FOREIGN KEY (user_id) REFERENCES users (id) + ON UPDATE CASCADE ON DELETE CASCADE, + CONSTRAINT FK_review_alarm_foodmarket + FOREIGN KEY (foodmarket_id) REFERENCES foodmarket (id) + ON UPDATE CASCADE ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='리뷰 알림'; + +-- 리뷰 이미지 +CREATE TABLE review_image ( + id BIGINT NOT NULL AUTO_INCREMENT COMMENT '고유 이미지 ID', + review_id BIGINT NOT NULL COMMENT '리뷰 ID', + url VARCHAR(200) NOT NULL COMMENT '이미지 URL', + PRIMARY KEY (id), + KEY IDX_review_image_review (review_id), + CONSTRAINT FK_review_image_review + FOREIGN KEY (review_id) REFERENCES review (id) + ON UPDATE CASCADE ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='리뷰 이미지들'; \ No newline at end of file diff --git a/week4/demo/src/main/java/com/example/demo/domain/alarm/entity/ReviewAlarm.java b/week4/demo/src/main/java/com/example/demo/domain/alarm/entity/ReviewAlarm.java new file mode 100644 index 0000000..2513f2b --- /dev/null +++ b/week4/demo/src/main/java/com/example/demo/domain/alarm/entity/ReviewAlarm.java @@ -0,0 +1,25 @@ +package com.example.demo.domain.alarm.entity; + +import com.example.demo.global.entity.BaseEntity; +import com.example.demo.domain.member.entity.Users; +import com.example.demo.domain.restruant.entity.FoodMarket; +import jakarta.persistence.*; +import lombok.*; + +@Entity @Table(name = "review_alarm") +@Getter @Setter @NoArgsConstructor @AllArgsConstructor +public class ReviewAlarm extends BaseEntity { + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(optional=false, fetch=FetchType.LAZY) + @JoinColumn(name="user_id", nullable=false) + private Users user; + + @ManyToOne(optional=false, fetch=FetchType.LAZY) + @JoinColumn(name="foodmarket_id", nullable=false) + private FoodMarket foodmarket; + + @Column(nullable=false, length=200) + private String content; +} diff --git a/week4/demo/src/main/java/com/example/demo/domain/member/entity/FavoriteFood.java b/week4/demo/src/main/java/com/example/demo/domain/member/entity/FavoriteFood.java new file mode 100644 index 0000000..cc7dac5 --- /dev/null +++ b/week4/demo/src/main/java/com/example/demo/domain/member/entity/FavoriteFood.java @@ -0,0 +1,29 @@ +package com.example.demo.domain.member.entity; + +import com.example.demo.global.entity.BaseEntity; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + + +@Entity +@Table(name ="favorite_food") +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class FavoriteFood extends BaseEntity { + + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(optional = false,fetch = FetchType.LAZY) + @JoinColumn(name = "food_id",nullable = false) + private Food food; + + @ManyToOne(optional = false,fetch = FetchType.LAZY) + @JoinColumn(name = "user_id",nullable = false) + private Users user; +} diff --git a/week4/demo/src/main/java/com/example/demo/domain/member/entity/Food.java b/week4/demo/src/main/java/com/example/demo/domain/member/entity/Food.java new file mode 100644 index 0000000..a396207 --- /dev/null +++ b/week4/demo/src/main/java/com/example/demo/domain/member/entity/Food.java @@ -0,0 +1,21 @@ +package com.example.demo.domain.member.entity; + +import com.example.demo.global.entity.BaseEntity; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Table(name = "food") +@Entity@Getter@Setter +@NoArgsConstructor@AllArgsConstructor +public class Food extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false,length = 40) + private String name; +} diff --git a/week4/demo/src/main/java/com/example/demo/domain/member/entity/Users.java b/week4/demo/src/main/java/com/example/demo/domain/member/entity/Users.java new file mode 100644 index 0000000..c9af3b0 --- /dev/null +++ b/week4/demo/src/main/java/com/example/demo/domain/member/entity/Users.java @@ -0,0 +1,40 @@ +package com.example.demo.domain.member.entity; + +import com.example.demo.global.entity.BaseEntity; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.time.LocalDate; + +@Entity +@Table(name = "users") +@Getter@Setter@NoArgsConstructor@AllArgsConstructor +public class Users extends BaseEntity { + + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false,length = 50) + private String email; + + @Column(nullable = false,length =100) + private String password; + + @Column(nullable = false,length = 20) + private String name; + + @Column(nullable = false) + private Byte gender; + + @Column(nullable = false) + private LocalDate birth; + + @Column(nullable = false,length = 50) + private String address; + + @Column(nullable = false) + private Integer point; +} diff --git a/week4/demo/src/main/java/com/example/demo/domain/member/repository/FavoriteFoodRepository.java b/week4/demo/src/main/java/com/example/demo/domain/member/repository/FavoriteFoodRepository.java new file mode 100644 index 0000000..0a1c94b --- /dev/null +++ b/week4/demo/src/main/java/com/example/demo/domain/member/repository/FavoriteFoodRepository.java @@ -0,0 +1,10 @@ +package com.example.demo.domain.member.repository; + +import com.example.demo.domain.member.entity.FavoriteFood; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface FavoriteFoodRepository extends JpaRepository { + +} diff --git a/week4/demo/src/main/java/com/example/demo/domain/member/repository/FoodRepository.java b/week4/demo/src/main/java/com/example/demo/domain/member/repository/FoodRepository.java new file mode 100644 index 0000000..a7aec29 --- /dev/null +++ b/week4/demo/src/main/java/com/example/demo/domain/member/repository/FoodRepository.java @@ -0,0 +1,9 @@ +package com.example.demo.domain.member.repository; + +import com.example.demo.domain.member.entity.Food; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface FoodRepository extends JpaRepository { +} diff --git a/week4/demo/src/main/java/com/example/demo/domain/member/repository/UsersRepository.java b/week4/demo/src/main/java/com/example/demo/domain/member/repository/UsersRepository.java new file mode 100644 index 0000000..10de128 --- /dev/null +++ b/week4/demo/src/main/java/com/example/demo/domain/member/repository/UsersRepository.java @@ -0,0 +1,15 @@ +package com.example.demo.domain.member.repository; + +import com.example.demo.domain.member.entity.Users; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; + +import java.util.Optional; +@Repository +public interface UsersRepository extends JpaRepository { + Optional findById(Long userId); + + @Query("select u from Users u WHERE u.id=:userId") + Optional findById2(Long userId); +} diff --git a/week4/demo/src/main/java/com/example/demo/domain/mission/entity/Mission.java b/week4/demo/src/main/java/com/example/demo/domain/mission/entity/Mission.java new file mode 100644 index 0000000..45cac8b --- /dev/null +++ b/week4/demo/src/main/java/com/example/demo/domain/mission/entity/Mission.java @@ -0,0 +1,23 @@ +package com.example.demo.domain.mission.entity; + +import com.example.demo.global.entity.BaseEntity; +import com.example.demo.domain.restruant.entity.FoodMarket; +import jakarta.persistence.*; +import lombok.*; + +@Entity @Table(name = "mission") +@Getter @Setter @NoArgsConstructor @AllArgsConstructor +public class Mission extends BaseEntity { + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(optional=false, fetch=FetchType.LAZY) + @JoinColumn(name="market_id", nullable=false) + private FoodMarket market; + + @Column(nullable=false, length=100) + private String content; + + @Column(name="mission_point", nullable=false) + private Integer missionPoint; +} \ No newline at end of file diff --git a/week4/demo/src/main/java/com/example/demo/domain/mission/entity/MissionUser.java b/week4/demo/src/main/java/com/example/demo/domain/mission/entity/MissionUser.java new file mode 100644 index 0000000..b5a6bae --- /dev/null +++ b/week4/demo/src/main/java/com/example/demo/domain/mission/entity/MissionUser.java @@ -0,0 +1,24 @@ +package com.example.demo.domain.mission.entity; + +import com.example.demo.global.entity.BaseEntity; +import com.example.demo.domain.member.entity.Users; +import jakarta.persistence.*; +import lombok.*; + +@Entity @Table(name = "mission_user") +@Getter @Setter @NoArgsConstructor @AllArgsConstructor +public class MissionUser extends BaseEntity { + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(optional=false, fetch=FetchType.LAZY) + @JoinColumn(name="mission_id", nullable=false) + private Mission mission; + + @ManyToOne(optional=false, fetch=FetchType.LAZY) + @JoinColumn(name="user_id", nullable=false) + private Users user; + + @Column(nullable=false, length=200) + private String content; +} diff --git a/week4/demo/src/main/java/com/example/demo/domain/mission/entity/PermissionMission.java b/week4/demo/src/main/java/com/example/demo/domain/mission/entity/PermissionMission.java new file mode 100644 index 0000000..5e13ede --- /dev/null +++ b/week4/demo/src/main/java/com/example/demo/domain/mission/entity/PermissionMission.java @@ -0,0 +1,21 @@ +package com.example.demo.domain.mission.entity; + +import com.example.demo.global.entity.BaseEntity; +import com.example.demo.domain.member.entity.Users; +import jakarta.persistence.*; +import lombok.*; + +@Entity @Table(name = "permission_mission") +@Getter @Setter @NoArgsConstructor @AllArgsConstructor +public class PermissionMission extends BaseEntity { + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(optional=false, fetch=FetchType.LAZY) + @JoinColumn(name="user_id", nullable=false) + private Users user; + + @ManyToOne(optional=false, fetch=FetchType.LAZY) + @JoinColumn(name="mission_id", nullable=false) + private Mission mission; +} \ No newline at end of file diff --git a/week4/demo/src/main/java/com/example/demo/domain/mission/repository/MissionRepository.java b/week4/demo/src/main/java/com/example/demo/domain/mission/repository/MissionRepository.java new file mode 100644 index 0000000..76b69bf --- /dev/null +++ b/week4/demo/src/main/java/com/example/demo/domain/mission/repository/MissionRepository.java @@ -0,0 +1,13 @@ +package com.example.demo.domain.mission.repository; + +import com.example.demo.domain.mission.entity.Mission; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + +import java.util.List; + +public interface MissionRepository extends JpaRepository { + List findMissionsByContentOrderByUpdatedAt(String Content); + @Query("select m from Mission m where m.content=:Content order by m.updatedAt desc") + List findMissionsByContentOrderByUpdatedAt2(String Content); +} diff --git a/week4/demo/src/main/java/com/example/demo/domain/mission/repository/MissionUserRepository.java b/week4/demo/src/main/java/com/example/demo/domain/mission/repository/MissionUserRepository.java new file mode 100644 index 0000000..f1d4445 --- /dev/null +++ b/week4/demo/src/main/java/com/example/demo/domain/mission/repository/MissionUserRepository.java @@ -0,0 +1,13 @@ +package com.example.demo.domain.mission.repository; + +import com.example.demo.domain.mission.entity.MissionUser; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + +import java.util.List; + +public interface MissionUserRepository extends JpaRepository { + List findByUser_IdOrderByUpdatedAtDesc(int userId); + @Query("select mu from MissionUser mu join fetch mu.mission where mu.user.id = :userId order by mu.updatedAt desc ") + List findByUser_IdOrderByUpdatedAtDesc2(int userId); +} diff --git a/week4/demo/src/main/java/com/example/demo/domain/restruant/entity/FoodMarket.java b/week4/demo/src/main/java/com/example/demo/domain/restruant/entity/FoodMarket.java new file mode 100644 index 0000000..fa2f740 --- /dev/null +++ b/week4/demo/src/main/java/com/example/demo/domain/restruant/entity/FoodMarket.java @@ -0,0 +1,24 @@ +package com.example.demo.domain.restruant.entity; + +import com.example.demo.global.entity.BaseEntity; +import jakarta.persistence.*; +import lombok.*; + +@Entity @Table(name = "foodmarket") +@Getter @Setter @NoArgsConstructor @AllArgsConstructor +public class FoodMarket extends BaseEntity { + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable=false, length=40) + private String name; + + @Column(nullable=false, length=50) + private String address; + + @Column(nullable=false) + private Boolean open; + + @Column(nullable=false, length=200) + private String content; +} diff --git a/week4/demo/src/main/java/com/example/demo/domain/restruant/entity/FoodMarketRepository.java b/week4/demo/src/main/java/com/example/demo/domain/restruant/entity/FoodMarketRepository.java new file mode 100644 index 0000000..5b715b6 --- /dev/null +++ b/week4/demo/src/main/java/com/example/demo/domain/restruant/entity/FoodMarketRepository.java @@ -0,0 +1,6 @@ +package com.example.demo.domain.restruant.entity; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface FoodMarketRepository extends JpaRepository { +} diff --git a/week4/demo/src/main/java/com/example/demo/domain/review/Exception/ReviewException.java b/week4/demo/src/main/java/com/example/demo/domain/review/Exception/ReviewException.java new file mode 100644 index 0000000..1160e75 --- /dev/null +++ b/week4/demo/src/main/java/com/example/demo/domain/review/Exception/ReviewException.java @@ -0,0 +1,10 @@ +package com.example.demo.domain.review.exception; + +import com.example.demo.global.apipayload.Exception.GeneralException; +import com.example.demo.domain.review.exception.code.ReviewErrorCode; + +public class ReviewException extends GeneralException { + public ReviewException(ReviewErrorCode errorCode) { + super(errorCode); + } +} diff --git a/week4/demo/src/main/java/com/example/demo/domain/review/Exception/code/ReviewErrorCode.java b/week4/demo/src/main/java/com/example/demo/domain/review/Exception/code/ReviewErrorCode.java new file mode 100644 index 0000000..1639185 --- /dev/null +++ b/week4/demo/src/main/java/com/example/demo/domain/review/Exception/code/ReviewErrorCode.java @@ -0,0 +1,19 @@ +package com.example.demo.domain.review.exception.code; + +import com.example.demo.global.apipayload.response.BaseErrorCode; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@AllArgsConstructor +@Getter +public enum ReviewErrorCode implements BaseErrorCode { + REVIEW_CREATE_ERROR(HttpStatus.BAD_REQUEST, "R001", "리뷰 등록을 실패했습니다."), + REVIEW_UPDATE_ERROR(HttpStatus.BAD_REQUEST, "R002", "리뷰 수정이 실패했습니다."), + REVIEW_DELETE_ERROR(HttpStatus.BAD_REQUEST,"R003", "리뷰가 삭제에 실패했습니다."), + REVIEW_MY_LIST_ERROR(HttpStatus.BAD_REQUEST, "R004", "내 리뷰 목록 조회에 실패했습니다."); + + private final HttpStatus httpStatus; + private final String code; + private final String message; +} diff --git a/week4/demo/src/main/java/com/example/demo/domain/review/Exception/code/ReviewSuccessCode.java b/week4/demo/src/main/java/com/example/demo/domain/review/Exception/code/ReviewSuccessCode.java new file mode 100644 index 0000000..9b94061 --- /dev/null +++ b/week4/demo/src/main/java/com/example/demo/domain/review/Exception/code/ReviewSuccessCode.java @@ -0,0 +1,25 @@ +// src/main/java/com/example/demo/global/response/SuccessCode.java +package com.example.demo.domain.review.exception.code; + +import com.example.demo.global.apipayload.response.BaseSuccessCode; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; +@Getter +@AllArgsConstructor +public enum ReviewSuccessCode implements BaseSuccessCode { + + // === Review 도메인 관련 응답 코드 === + REVIEW_CREATE_SUCCESS(HttpStatus.OK, "R001", "리뷰가 성공적으로 등록되었습니다."), + REVIEW_UPDATE_SUCCESS(HttpStatus.OK, "R002", "리뷰가 성공적으로 수정되었습니다."), + REVIEW_DELETE_SUCCESS(HttpStatus.NO_CONTENT,"R003", "리뷰가 성공적으로 삭제되었습니다."), + REVIEW_MY_LIST_SUCCESS(HttpStatus.OK, "R004", "내 리뷰 목록 조회에 성공했습니다."); + + private final HttpStatus httpStatus; + private final String code; + private final String message; + + + + +} diff --git a/week4/demo/src/main/java/com/example/demo/domain/review/Handler/ReviewExceptionHandler.java b/week4/demo/src/main/java/com/example/demo/domain/review/Handler/ReviewExceptionHandler.java new file mode 100644 index 0000000..cf8a6c3 --- /dev/null +++ b/week4/demo/src/main/java/com/example/demo/domain/review/Handler/ReviewExceptionHandler.java @@ -0,0 +1,19 @@ +package com.example.demo.domain.review.handler; + +import com.example.demo.domain.review.exception.ReviewException; +import com.example.demo.global.apipayload.response.ApiResponse; +import com.example.demo.domain.review.exception.code.ReviewErrorCode; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +@RestControllerAdvice +public class ReviewExceptionHandler { + + @ExceptionHandler(ReviewException.class) + public ResponseEntity> handleReviewException(ReviewException e) { + ReviewErrorCode errorCode =e.getErrorCode(); + return ApiResponse.ERROR(errorCode); + } + + +} diff --git a/week4/demo/src/main/java/com/example/demo/domain/review/controller/ReviewController.java b/week4/demo/src/main/java/com/example/demo/domain/review/controller/ReviewController.java new file mode 100644 index 0000000..23e12c6 --- /dev/null +++ b/week4/demo/src/main/java/com/example/demo/domain/review/controller/ReviewController.java @@ -0,0 +1,56 @@ +// src/main/java/com/example/demo/controller/ReviewController.java +package com.example.demo.domain.review.controller; + +import com.example.demo.domain.review.dto.ReviewCreateRequest; +import com.example.demo.domain.review.dto.ReviewDto; +import com.example.demo.domain.review.dto.ReviewResponse; +import com.example.demo.domain.review.service.ReviewService; +import com.example.demo.global.apipayload.response.ApiResponse; +import com.example.demo.domain.review.exception.code.ReviewSuccessCode; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +@RequiredArgsConstructor +@RestController +@RequestMapping("/api/reviews") +public class ReviewController { + + private final ReviewService service; + + + @PostMapping + public ResponseEntity> createReview( + @Valid @RequestBody ReviewCreateRequest request + ){ + ReviewResponse reviewResponse=service.createReview(request); + return ApiResponse.success(ReviewSuccessCode.REVIEW_CREATE_SUCCESS,reviewResponse); + } + @PutMapping("/{reviewId}") + public ResponseEntity> updateReview( + @PathVariable Long reviewId, + @RequestBody ReviewCreateRequest request + ){ + ReviewResponse reviewResponse = service.updateReview(request.userId(), reviewId, request); + return ApiResponse.success(ReviewSuccessCode.REVIEW_UPDATE_SUCCESS,reviewResponse); + } + @DeleteMapping("/{reviewId}") + public ResponseEntity> deleteReview( + @PathVariable Long reviewId, + @RequestParam Long userId + ) { + service.deleteReview(reviewId, userId); + return ApiResponse.success(ReviewSuccessCode.REVIEW_DELETE_SUCCESS); + } + @GetMapping("/my") + public ResponseEntity>> myReviews( + @RequestParam Long userId, + @RequestParam(required = false) String marketName, + @RequestParam(required = false) Integer starBand + ) { + List result = service.findMyReviews(userId, marketName, starBand); + return ApiResponse.success(ReviewSuccessCode.REVIEW_MY_LIST_SUCCESS, result); + } +} diff --git a/week4/demo/src/main/java/com/example/demo/domain/review/dto/ReviewCreateRequest.java b/week4/demo/src/main/java/com/example/demo/domain/review/dto/ReviewCreateRequest.java new file mode 100644 index 0000000..4edb4a5 --- /dev/null +++ b/week4/demo/src/main/java/com/example/demo/domain/review/dto/ReviewCreateRequest.java @@ -0,0 +1,21 @@ +package com.example.demo.domain.review.dto; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +import java.math.BigDecimal; +import java.util.List; +/* +* 나는 이 친구로 받을 데이터를 정의 +*/ +public record ReviewCreateRequest( + @NotNull @Min(0) + Long userId, + @NotNull @Min(0) + Long marketId, + @NotBlank + String content, + BigDecimal star, + List imageUrls // optional +) {} diff --git a/week4/demo/src/main/java/com/example/demo/domain/review/dto/ReviewDto.java b/week4/demo/src/main/java/com/example/demo/domain/review/dto/ReviewDto.java new file mode 100644 index 0000000..9b08333 --- /dev/null +++ b/week4/demo/src/main/java/com/example/demo/domain/review/dto/ReviewDto.java @@ -0,0 +1,17 @@ +package com.example.demo.domain.review.dto; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +import java.math.BigDecimal; + +/* +* 이친구는 이미지는 안뿌려주는 dto에요. +*/ +public record ReviewDto( + Long reviewId, + String marketName, + String content, + BigDecimal star +) {} diff --git a/week4/demo/src/main/java/com/example/demo/domain/review/dto/ReviewResponse.java b/week4/demo/src/main/java/com/example/demo/domain/review/dto/ReviewResponse.java new file mode 100644 index 0000000..872e9a7 --- /dev/null +++ b/week4/demo/src/main/java/com/example/demo/domain/review/dto/ReviewResponse.java @@ -0,0 +1,16 @@ +package com.example.demo.domain.review.dto; + +import java.math.BigDecimal; +import java.util.List; +/* +* 나는 이 친구로 이미지까지 뿌려줄 가장 디테일한 dto를 정의 +* */ +public record ReviewResponse( + Long reviewId, + Long userId, + Long marketId, + String marketName, + String content, + BigDecimal star, + List imageUrls +) {} \ No newline at end of file diff --git a/week4/demo/src/main/java/com/example/demo/domain/review/entity/Review.java b/week4/demo/src/main/java/com/example/demo/domain/review/entity/Review.java new file mode 100644 index 0000000..de12504 --- /dev/null +++ b/week4/demo/src/main/java/com/example/demo/domain/review/entity/Review.java @@ -0,0 +1,32 @@ +package com.example.demo.domain.review.entity; + + +import com.example.demo.global.entity.BaseEntity; +import com.example.demo.domain.member.entity.Users; +import com.example.demo.domain.restruant.entity.FoodMarket; +import jakarta.persistence.*; +import lombok.*; + +import java.math.BigDecimal; + +@Entity @Table(name = "review") +@Getter @Setter @NoArgsConstructor @AllArgsConstructor +public class Review extends BaseEntity { + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(optional=false, fetch=FetchType.LAZY) + @JoinColumn(name="user_id", nullable=false) + private Users user; + + @ManyToOne(optional=false, fetch=FetchType.LAZY) + @JoinColumn(name="market_id", nullable=false) + private FoodMarket market; + + @Column(nullable=false, length=100) + private String content; + + @Column(nullable=false, precision=2, scale=1) + private BigDecimal star; +} + diff --git a/week4/demo/src/main/java/com/example/demo/domain/review/entity/ReviewImage.java b/week4/demo/src/main/java/com/example/demo/domain/review/entity/ReviewImage.java new file mode 100644 index 0000000..11ff6c4 --- /dev/null +++ b/week4/demo/src/main/java/com/example/demo/domain/review/entity/ReviewImage.java @@ -0,0 +1,19 @@ +package com.example.demo.domain.review.entity; + +import com.example.demo.global.entity.BaseEntity; +import jakarta.persistence.*; +import lombok.*; + +@Entity @Table(name = "review_image") +@Getter @Setter @NoArgsConstructor @AllArgsConstructor +public class ReviewImage extends BaseEntity { + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(optional=false, fetch=FetchType.LAZY) + @JoinColumn(name="review_id", nullable=false) + private Review review; + + @Column(nullable=false, length=200) + private String url; +} \ No newline at end of file diff --git a/week4/demo/src/main/java/com/example/demo/domain/review/repository/ReviewImageRepository.java b/week4/demo/src/main/java/com/example/demo/domain/review/repository/ReviewImageRepository.java new file mode 100644 index 0000000..661c52c --- /dev/null +++ b/week4/demo/src/main/java/com/example/demo/domain/review/repository/ReviewImageRepository.java @@ -0,0 +1,13 @@ +package com.example.demo.domain.review.repository; + +import com.example.demo.domain.review.entity.Review; +import com.example.demo.domain.review.entity.ReviewImage; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; +@Repository +public interface ReviewImageRepository extends JpaRepository { + List findByReview(Review review); + void deleteByReview(Review review); +} diff --git a/week4/demo/src/main/java/com/example/demo/domain/review/repository/ReviewRepository.java b/week4/demo/src/main/java/com/example/demo/domain/review/repository/ReviewRepository.java new file mode 100644 index 0000000..8c2d098 --- /dev/null +++ b/week4/demo/src/main/java/com/example/demo/domain/review/repository/ReviewRepository.java @@ -0,0 +1,15 @@ +package com.example.demo.domain.review.repository; + +import com.example.demo.domain.review.entity.Review; +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.util.List; +@Repository +public interface ReviewRepository extends JpaRepository { + List findReviewsByMarket_IdOrderByUpdatedAtDesc(Long marketId); + @Query("SELECT r from Review r join fetch r.user where r.market.id=:marketId order by r.updatedAt desc ") + List findReviewsByMarket_IdOrderByUpdatedAtDesc2(@Param("marketId") Long marketId); +} diff --git a/week4/demo/src/main/java/com/example/demo/domain/review/service/ReviewService.java b/week4/demo/src/main/java/com/example/demo/domain/review/service/ReviewService.java new file mode 100644 index 0000000..172c51a --- /dev/null +++ b/week4/demo/src/main/java/com/example/demo/domain/review/service/ReviewService.java @@ -0,0 +1,172 @@ +package com.example.demo.domain.review.service; + +import com.example.demo.domain.member.entity.Users; +import com.example.demo.domain.member.repository.UsersRepository; +import com.example.demo.domain.restruant.entity.FoodMarket; +import com.example.demo.domain.restruant.entity.FoodMarketRepository; +import com.example.demo.domain.review.dto.ReviewCreateRequest; +import com.example.demo.domain.review.dto.ReviewDto; +import com.example.demo.domain.review.dto.ReviewResponse; +import com.example.demo.domain.review.entity.Review; +import com.example.demo.domain.review.entity.ReviewImage; +import com.example.demo.domain.review.repository.ReviewImageRepository; +import com.example.demo.domain.review.repository.ReviewRepository; +import com.example.demo.domain.review.exception.ReviewException; +import com.example.demo.domain.review.exception.code.ReviewErrorCode; +import com.querydsl.core.types.Projections; +import com.querydsl.core.types.dsl.BooleanExpression; +import com.querydsl.jpa.impl.JPAQueryFactory; +import jakarta.transaction.Transactional; +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; + +import java.util.Collections; +import java.util.List; + +import static com.example.demo.domain.restruant.entity.QFoodMarket.foodMarket; +import static com.example.demo.domain.review.entity.QReview.review; + + +@Service +@AllArgsConstructor +public class ReviewService { + private final JPAQueryFactory qf; + private final ReviewRepository reviewRepository; + private final ReviewImageRepository reviewImageRepository; + private final UsersRepository usersRepository; + private final FoodMarketRepository foodMarketRepository; + + @Transactional + public ReviewResponse createReview(ReviewCreateRequest request){ + Users user=usersRepository.findById(request.userId()) + .orElseThrow(()->new ReviewException(ReviewErrorCode.REVIEW_CREATE_ERROR)); + FoodMarket market = foodMarketRepository.findById(request.marketId()) + .orElseThrow(()->new ReviewException(ReviewErrorCode.REVIEW_CREATE_ERROR)); + Review reviewEntity = new Review(); + reviewEntity.setUser(user); + reviewEntity.setMarket(market); + reviewEntity.setContent(request.content()); + reviewEntity.setStar(request.star()); + + Review saved = reviewRepository.save(reviewEntity); + + if(request.imageUrls()!=null&& !request.imageUrls().isEmpty()){ + for(String url:request.imageUrls()){ + ReviewImage reviewImage=new ReviewImage(); + reviewImage.setReview(saved); + reviewImage.setUrl(url); + reviewImageRepository.save(reviewImage); + } + } + + return toResponse(saved); + } + + @Transactional + public ReviewResponse updateReview(Long reviewId, Long userId,ReviewCreateRequest request) { + + Review reviewEntity = reviewRepository.findById(reviewId) + .orElseThrow(()->new ReviewException(ReviewErrorCode.REVIEW_UPDATE_ERROR)); + + if(!reviewEntity.getUser().getId().equals(userId)){ + new ReviewException(ReviewErrorCode.REVIEW_UPDATE_ERROR); + } + + reviewEntity.setContent(request.content()); + reviewEntity.setStar(request.star()); + + reviewImageRepository.deleteByReview(reviewEntity); + + if(request.imageUrls()!=null&& !request.imageUrls().isEmpty()){ + for(String url:request.imageUrls()){ + ReviewImage reviewImage=new ReviewImage(); + reviewImage.setReview(reviewEntity); + reviewImage.setUrl(url); + reviewImageRepository.save(reviewImage); + } + } + return toResponse(reviewEntity); + } + public void deleteReview(Long reviewId,Long userId) { + + Review reviewEntity = reviewRepository.findById(reviewId) + .orElseThrow(()->new ReviewException(ReviewErrorCode.REVIEW_DELETE_ERROR)); + + if(!reviewEntity.getUser().getId().equals(userId)){ + throw new ReviewException(ReviewErrorCode.REVIEW_DELETE_ERROR); + } + reviewRepository.delete(reviewEntity); + reviewImageRepository.deleteByReview(reviewEntity); + } + + public List findMyReviews(Long userId, String marketName, Integer starBand) { + // userId should be required; validate if needed + return qf.select(Projections.constructor(ReviewDto.class, + review.id, + review.market.name, + review.content, + review.star)) + .from(review) + .join(review.market, foodMarket) + .where( + review.user.id.eq(userId), + marketNameEquals(marketName), + starBandPredicate(starBand) + ) + .orderBy(review.id.desc()) + .fetch(); + } + + public List findReviewsByMarket(Long marketId, Integer starBand) { + List reviews = qf.selectFrom(review) + .leftJoin(review.market).fetchJoin() + .leftJoin(review.user).fetchJoin() + .where( + review.market.id.eq(marketId), + starBandPredicate(starBand) + ) + .orderBy(review.id.desc()) + .fetch(); + + return reviews.stream() + .map(this::toResponse) + .toList(); + } + + private BooleanExpression starBandPredicate(Integer band) { + if (band == null) return null; + if (band == 5) return review.star.eq(new BigDecimal("5.0")); + if (band >= 0 && band <= 4) { + BigDecimal min = new BigDecimal(band + ".0"); + BigDecimal max = new BigDecimal((band + 1) + ".0"); + return review.star.goe(min).and(review.star.lt(max)); + } + return null; + } + private BooleanExpression marketNameEquals(String marketName) { + return (marketName == null || marketName.isBlank()) + ? null + : review.market.name.eq(marketName); + } + private ReviewResponse toResponse(Review reviewEntity) { + List urls = reviewEntity.getId() == null + ? Collections.emptyList() + : reviewImageRepository.findByReview(reviewEntity) + .stream() + .map(ReviewImage::getUrl) + .toList(); + + return new ReviewResponse( + reviewEntity.getId(), + reviewEntity.getUser().getId(), + reviewEntity.getMarket().getId(), + reviewEntity.getMarket().getName(), + reviewEntity.getContent(), + reviewEntity.getStar(), + urls + ); + } +} + diff --git a/week4/demo/src/main/java/com/example/demo/global/apiPayload/Exception/GeneralException.java b/week4/demo/src/main/java/com/example/demo/global/apiPayload/Exception/GeneralException.java new file mode 100644 index 0000000..6cb0e3b --- /dev/null +++ b/week4/demo/src/main/java/com/example/demo/global/apiPayload/Exception/GeneralException.java @@ -0,0 +1,16 @@ +package com.example.demo.global.apipayload.Exception; + +import com.example.demo.domain.review.exception.code.ReviewErrorCode; +import lombok.Getter; + +@Getter +public class GeneralException extends RuntimeException { + + private final ReviewErrorCode errorCode; + + public GeneralException(ReviewErrorCode errorCode) { + super(errorCode.getMessage()); + this.errorCode = errorCode; + } + +} diff --git a/week4/demo/src/main/java/com/example/demo/global/apiPayload/response/ApiResponse.java b/week4/demo/src/main/java/com/example/demo/global/apiPayload/response/ApiResponse.java new file mode 100644 index 0000000..5234e46 --- /dev/null +++ b/week4/demo/src/main/java/com/example/demo/global/apiPayload/response/ApiResponse.java @@ -0,0 +1,60 @@ + +package com.example.demo.global.apipayload.response; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.ResponseEntity; +@Getter +@AllArgsConstructor +public class ApiResponse { + + private final boolean success; // 성공 여부 + private final String code; // 비즈니스 코드 (예: R001) + private final String message; // 기본 메시지 + private final T data; // 실제 데이터 (ReviewResponse, List 등) + + + + // data 있는 성공 + public static ResponseEntity> success(BaseSuccessCode code, T data) { + ApiResponse body = new ApiResponse<>( + true, + code.getCode(), + code.getMessage(), + data + ); + return ResponseEntity.status(code.getHttpStatus()).body(body); + } + + // data 없는 성공 (DELETE 등) + public static ResponseEntity> success(BaseSuccessCode code) { + ApiResponse body = new ApiResponse<>( + true, + code.getCode(), + code.getMessage(), + null + ); + return ResponseEntity.status(code.getHttpStatus()).body(body); + } + // data 있는 실패 + public static ResponseEntity> ERROR(BaseErrorCode code, T data) { + ApiResponse body = new ApiResponse<>( + false, + code.getCode(), + code.getMessage(), + data + ); + + return ResponseEntity.status(code.getHttpStatus()).body(body); + } + + // data 없는 실패(DELETE 등) + public static ResponseEntity> ERROR(BaseErrorCode code) { + ApiResponse body = new ApiResponse<>( + false, + code.getCode(), + code.getMessage(), + null + ); + return ResponseEntity.status(code.getHttpStatus()).body(body); + } +} diff --git a/week4/demo/src/main/java/com/example/demo/global/apiPayload/response/BaseErrorCode.java b/week4/demo/src/main/java/com/example/demo/global/apiPayload/response/BaseErrorCode.java new file mode 100644 index 0000000..9f1693e --- /dev/null +++ b/week4/demo/src/main/java/com/example/demo/global/apiPayload/response/BaseErrorCode.java @@ -0,0 +1,9 @@ +package com.example.demo.global.apipayload.response; + +import org.springframework.http.HttpStatus; + +public interface BaseErrorCode { + HttpStatus getHttpStatus(); + String getMessage(); + String getCode(); +} diff --git a/week4/demo/src/main/java/com/example/demo/global/apiPayload/response/BaseSuccessCode.java b/week4/demo/src/main/java/com/example/demo/global/apiPayload/response/BaseSuccessCode.java new file mode 100644 index 0000000..35e2015 --- /dev/null +++ b/week4/demo/src/main/java/com/example/demo/global/apiPayload/response/BaseSuccessCode.java @@ -0,0 +1,8 @@ +package com.example.demo.global.apipayload.response; +import org.springframework.http.HttpStatus; + +public interface BaseSuccessCode { + HttpStatus getHttpStatus(); + String getMessage(); + String getCode(); +} diff --git a/week4/demo/src/main/java/com/example/demo/global/config/QuerydslConfig.java b/week4/demo/src/main/java/com/example/demo/global/config/QuerydslConfig.java new file mode 100644 index 0000000..a54c2ed --- /dev/null +++ b/week4/demo/src/main/java/com/example/demo/global/config/QuerydslConfig.java @@ -0,0 +1,15 @@ +package com.example.demo.global.config; + +import com.querydsl.jpa.impl.JPAQueryFactory; +import jakarta.persistence.EntityManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class QuerydslConfig { + + @Bean + public JPAQueryFactory jpaQueryFactory(EntityManager entityManager) { + return new JPAQueryFactory(entityManager); + } +} diff --git a/week4/demo/src/main/java/com/example/demo/global/config/SwaggerConfig.java b/week4/demo/src/main/java/com/example/demo/global/config/SwaggerConfig.java new file mode 100644 index 0000000..914ff7b --- /dev/null +++ b/week4/demo/src/main/java/com/example/demo/global/config/SwaggerConfig.java @@ -0,0 +1,19 @@ +package com.example.demo.global.config; + +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.OpenAPI; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class SwaggerConfig { + + @Bean + public OpenAPI openAPI() { + return new OpenAPI() + .info(new Info() + .title("Project API") + .description("Project Swagger") + .version("0.0.1")); + } +} diff --git a/week4/demo/src/main/java/com/example/demo/global/entity/BaseEntity.java b/week4/demo/src/main/java/com/example/demo/global/entity/BaseEntity.java new file mode 100644 index 0000000..282a93e --- /dev/null +++ b/week4/demo/src/main/java/com/example/demo/global/entity/BaseEntity.java @@ -0,0 +1,25 @@ +package com.example.demo.global.entity; + +import jakarta.persistence.Column; +import jakarta.persistence.EntityListeners; +import jakarta.persistence.MappedSuperclass; +import lombok.Getter; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import java.time.Instant; + +@MappedSuperclass +@EntityListeners(AuditingEntityListener.class)//이걸 해야 저게 자동 기록됨. +@Getter +public abstract class BaseEntity { + + @CreatedDate + @Column(nullable = false, updatable = false) + private Instant createdAt; + + @LastModifiedDate + @Column(nullable = false) + private Instant updatedAt; +} \ No newline at end of file diff --git a/week4/demo/src/main/resources/application.yml b/week4/demo/src/main/resources/application.yml new file mode 100644 index 0000000..244a159 --- /dev/null +++ b/week4/demo/src/main/resources/application.yml @@ -0,0 +1,16 @@ +spring: + application: + name :demo + + datasource: + driver-class-name: com.mysql.cj.jdbc.Driver #mysql???.. + url: ${DB_URL} + username: ${DB_USERNAME} + password: ${DB_PASSWORD} + + jpa: + hibernate: + ddl-auto: create-drop + database: mysql + database-platform: org.hibernate.dialect.MySQLDialect + show-sql: true \ No newline at end of file diff --git a/week4/demo/src/test/java/com/example/demo/DemoApplicationTests.java b/week4/demo/src/test/java/com/example/demo/DemoApplicationTests.java new file mode 100644 index 0000000..2778a6a --- /dev/null +++ b/week4/demo/src/test/java/com/example/demo/DemoApplicationTests.java @@ -0,0 +1,13 @@ +package com.example.demo; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class DemoApplicationTests { + + @Test + void contextLoads() { + } + +}