diff --git a/configs/common.go b/configs/common.go index 246840b..9203709 100644 --- a/configs/common.go +++ b/configs/common.go @@ -4,6 +4,7 @@ const ( BASE_URL = "http://localhost:3030" PORT = ":3030" DATABASE_URL = "learnsteam:fbOgZ6Xxn5VXBYihjqygRXyaK6ZUgKL6@tcp(localhost:3306)/learnsteam_quiz?charset=utf8&parseTime=True&loc=Local" + //DATABASE_URL = "root:fbOgZ6Xxn5VXBYihjqygRXyaK6ZUgKL6@tcp(learnsteam-quiz-db:3306)/learnsteam_quiz?charset=utf8&parseTime=True&loc=Local" KAKAO_CLIENT_ID = "986830" KAKAO_CLIENT_SECRET = "14f63a8e91c4e0fe88bc40e3ff348233" diff --git a/configs/common.local b/configs/common.local index 6d3914c..9203709 100644 --- a/configs/common.local +++ b/configs/common.local @@ -3,8 +3,8 @@ package config const ( BASE_URL = "http://localhost:3030" PORT = ":3030" - //DATABASE_URL = "learnsteam:fbOgZ6Xxn5VXBYihjqygRXyaK6ZUgKL6@tcp(localhost:3306)/learnsteam_quiz?charset=utf8&parseTime=True&loc=Local" - DATABASE_URL = "root:fbOgZ6Xxn5VXBYihjqygRXyaK6ZUgKL6@tcp(learnsteam-quiz-db:3306)/learnsteam_quiz?charset=utf8&parseTime=True&loc=Local" + DATABASE_URL = "learnsteam:fbOgZ6Xxn5VXBYihjqygRXyaK6ZUgKL6@tcp(localhost:3306)/learnsteam_quiz?charset=utf8&parseTime=True&loc=Local" + //DATABASE_URL = "root:fbOgZ6Xxn5VXBYihjqygRXyaK6ZUgKL6@tcp(learnsteam-quiz-db:3306)/learnsteam_quiz?charset=utf8&parseTime=True&loc=Local" KAKAO_CLIENT_ID = "986830" KAKAO_CLIENT_SECRET = "14f63a8e91c4e0fe88bc40e3ff348233" diff --git a/docs/docs.go b/docs/docs.go index b3dc68f..bb0a0cf 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -20,7 +20,7 @@ const docTemplate = `{ "paths": { "/auth/login": { "post": { - "description": "username, password 를 입력하여 로그인", + "description": "사용자가 로그인합니다.", "consumes": [ "application/json" ], @@ -30,24 +30,15 @@ const docTemplate = `{ "tags": [ "로그인" ], - "summary": "로그인", + "summary": "사용자 로그인 로그인", "parameters": [ { - "description": "username", - "name": "username", + "description": "Login Body", + "name": "loginBody", "in": "body", "required": true, "schema": { - "type": "string" - } - }, - { - "description": "비밀번호", - "name": "password", - "in": "body", - "required": true, - "schema": { - "type": "string" + "$ref": "#/definitions/learnsteam_learsteam-quiz-api_internal_models.LoginRequest" } } ], @@ -76,30 +67,12 @@ const docTemplate = `{ "summary": "회원가입", "parameters": [ { - "description": "username", - "name": "username", + "description": "Register Body", + "name": "registerBody", "in": "body", "required": true, "schema": { - "type": "string" - } - }, - { - "description": "이름", - "name": "name", - "in": "body", - "required": true, - "schema": { - "type": "string" - } - }, - { - "description": "비밀번호", - "name": "password", - "in": "body", - "required": true, - "schema": { - "type": "string" + "$ref": "#/definitions/learnsteam_learsteam-quiz-api_internal_models.RegisterRequest" } } ], @@ -113,6 +86,177 @@ const docTemplate = `{ } } }, + "/exam": { + "get": { + "description": "응시 목록을 가져옵니다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Exam" + ], + "summary": "응시 목록 가져오기", + "parameters": [ + { + "type": "string", + "description": "검색어", + "name": "q", + "in": "query" + }, + { + "type": "integer", + "description": "페이지", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "페이지 사이즈", + "name": "limit", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/learnsteam_learsteam-quiz-api_internal_models.ExamListResponse" + } + } + } + }, + "put": { + "description": "퀴즈 프로그램을 수정합니다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Exam" + ], + "summary": "퀴즈 프로그램 수정", + "parameters": [ + { + "description": "Exam Update Body", + "name": "examUpdateBody", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/learnsteam_learsteam-quiz-api_internal_models.ExamUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/learnsteam_learsteam-quiz-api_internal_models.ExamResponse" + } + } + } + }, + "post": { + "description": "응시 매칭을 만듭니다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Exam" + ], + "summary": "응시 매칭 생성", + "parameters": [ + { + "description": "Exam Body", + "name": "examBody", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/learnsteam_learsteam-quiz-api_internal_models.ExamRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/learnsteam_learsteam-quiz-api_internal_models.ExamResponse" + } + } + } + }, + "patch": { + "description": "퀴즈 프로그램 정보를 변경합니다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Exam" + ], + "summary": "퀴즈 프로그램 정보 변경", + "parameters": [ + { + "description": "Exam Patch Body (변경할 필드만 입력)", + "name": "examPatchBody", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/learnsteam_learsteam-quiz-api_internal_models.ExamPatchRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/learnsteam_learsteam-quiz-api_internal_models.ExamResponse" + } + } + } + } + }, + "/exam/{id}": { + "get": { + "description": "ID로 응시 정보를 가져옵니다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Exam" + ], + "summary": "응시(시험) 정보 가져오기", + "parameters": [ + { + "type": "string", + "description": "응시 ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/learnsteam_learsteam-quiz-api_internal_models.ExamResponse" + } + } + } + } + }, "/program": { "get": { "description": "퀴즈 프로그램 목록을 가져옵니다.", @@ -175,60 +319,12 @@ const docTemplate = `{ "summary": "퀴즈 프로그램 수정", "parameters": [ { - "description": "프로그램 제목", - "name": "subject", + "description": "Program Body", + "name": "programUpdateBody", "in": "body", "required": true, "schema": { - "type": "string" - } - }, - { - "description": "프로그램 코스", - "name": "course", - "in": "body", - "required": true, - "schema": { - "type": "string" - } - }, - { - "description": "프로그램 내용", - "name": "content", - "in": "body", - "required": true, - "schema": { - "type": "string" - } - }, - { - "description": "프로그램 태그", - "name": "tag", - "in": "body", - "required": true, - "schema": { - "type": "array", - "items": { - "type": "string" - } - } - }, - { - "description": "프로그램 상태 on 또는 off", - "name": "status", - "in": "body", - "required": true, - "schema": { - "type": "string" - } - }, - { - "description": "프로그램 발행 날짜", - "name": "publish_at", - "in": "body", - "required": true, - "schema": { - "type": "string" + "$ref": "#/definitions/learnsteam_learsteam-quiz-api_internal_models.ProgramRequest" } } ], @@ -255,60 +351,12 @@ const docTemplate = `{ "summary": "퀴즈 프로그램 생성", "parameters": [ { - "description": "프로그램 제목", - "name": "subject", + "description": "Program Body", + "name": "programBody", "in": "body", "required": true, "schema": { - "type": "string" - } - }, - { - "description": "프로그램 코스", - "name": "course", - "in": "body", - "required": true, - "schema": { - "type": "string" - } - }, - { - "description": "프로그램 내용", - "name": "content", - "in": "body", - "required": true, - "schema": { - "type": "string" - } - }, - { - "description": "프로그램 태그", - "name": "tag", - "in": "body", - "required": true, - "schema": { - "type": "array", - "items": { - "type": "string" - } - } - }, - { - "description": "프로그램 상태 on 또는 off", - "name": "status", - "in": "body", - "required": true, - "schema": { - "type": "string" - } - }, - { - "description": "프로그램 발행 날짜", - "name": "publish_at", - "in": "body", - "required": true, - "schema": { - "type": "string" + "$ref": "#/definitions/learnsteam_learsteam-quiz-api_internal_models.ProgramRequest" } } ], @@ -417,88 +465,12 @@ const docTemplate = `{ "summary": "퀴즈 수정", "parameters": [ { - "type": "string", - "description": "퀴즈 ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "프로그램 ID", - "name": "program_id", + "description": "Quiz Body", + "name": "quizBody", "in": "body", "required": true, "schema": { - "type": "string" - } - }, - { - "description": "퀴즈 순서", - "name": "sequence", - "in": "body", - "required": true, - "schema": { - "type": "integer" - } - }, - { - "description": "퀴즈 타입 : choice, check, ox, input", - "name": "quiz_type", - "in": "body", - "required": true, - "schema": { - "type": "string" - } - }, - { - "description": "퀴즈 문제", - "name": "question", - "in": "body", - "required": true, - "schema": { - "type": "string" - } - }, - { - "description": "퀴즈 선택지 : ['선택1','선택2','선택3', '선택4']", - "name": "choice", - "in": "body", - "required": true, - "schema": { - "type": "array", - "items": { - "type": "string" - } - } - }, - { - "description": "퀴즈 정답 : [1,3]", - "name": "answer", - "in": "body", - "required": true, - "schema": { - "type": "array", - "items": { - "type": "string" - } - } - }, - { - "description": "퀴즈 힌트", - "name": "hint", - "in": "body", - "required": true, - "schema": { - "type": "string" - } - }, - { - "description": "퀴즈 해설", - "name": "comment", - "in": "body", - "required": true, - "schema": { - "type": "string" + "$ref": "#/definitions/learnsteam_learsteam-quiz-api_internal_models.QuizRequest" } } ], @@ -525,81 +497,12 @@ const docTemplate = `{ "summary": "퀴즈 생성", "parameters": [ { - "description": "프로그램 ID", - "name": "program_id", + "description": "Quiz Body", + "name": "quizBody", "in": "body", "required": true, "schema": { - "type": "string" - } - }, - { - "description": "퀴즈 순서", - "name": "sequence", - "in": "body", - "required": true, - "schema": { - "type": "integer" - } - }, - { - "description": "퀴즈 타입 : choice, check, ox, input", - "name": "quiz_type", - "in": "body", - "required": true, - "schema": { - "type": "string" - } - }, - { - "description": "퀴즈 문제", - "name": "question", - "in": "body", - "required": true, - "schema": { - "type": "string" - } - }, - { - "description": "퀴즈 선택지 : ['선택1','선택2','선택3', '선택4']", - "name": "choice", - "in": "body", - "required": true, - "schema": { - "type": "array", - "items": { - "type": "string" - } - } - }, - { - "description": "퀴즈 정답 : [1,3]", - "name": "answer", - "in": "body", - "required": true, - "schema": { - "type": "array", - "items": { - "type": "string" - } - } - }, - { - "description": "퀴즈 힌트", - "name": "hint", - "in": "body", - "required": true, - "schema": { - "type": "string" - } - }, - { - "description": "퀴즈 해설", - "name": "comment", - "in": "body", - "required": true, - "schema": { - "type": "string" + "$ref": "#/definitions/learnsteam_learsteam-quiz-api_internal_models.QuizRequest" } } ], @@ -756,6 +659,221 @@ const docTemplate = `{ } }, "definitions": { + "learnsteam_learsteam-quiz-api_internal_models.Exam": { + "type": "object", + "properties": { + "end_at": { + "type": "string" + }, + "id": { + "type": "string", + "example": "ef74c59a-c707-4162-a52b-455906c81ec1" + }, + "name": { + "type": "string", + "example": "홍길순" + }, + "program": { + "type": "string", + "example": "파이썬 초급 과정" + }, + "program_id": { + "type": "string", + "example": "7f9329f5-2e36-4638-92d2-73064b7291a4" + }, + "score": { + "type": "integer", + "example": 5 + }, + "start_at": { + "type": "string" + }, + "status": { + "type": "string", + "example": "ready" + }, + "subject": { + "type": "string", + "example": "출력 Print" + }, + "total": { + "type": "integer", + "example": 5 + }, + "user_id": { + "type": "string", + "example": "f95159dd-c42c-490d-ac6b-ca5d88a266bb" + } + } + }, + "learnsteam_learsteam-quiz-api_internal_models.ExamListResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/learnsteam_learsteam-quiz-api_internal_models.Exam" + } + }, + "page": { + "type": "integer", + "example": 1 + }, + "pageSize": { + "type": "integer", + "example": 10 + }, + "total": { + "type": "integer", + "example": 999 + }, + "totalPage": { + "type": "integer", + "example": 99 + } + } + }, + "learnsteam_learsteam-quiz-api_internal_models.ExamPatchRequest": { + "type": "object", + "properties": { + "end_at": { + "type": "string", + "example": "2023-11-10T13:25:00+09:00" + }, + "score": { + "type": "integer", + "example": 4 + }, + "start_at": { + "type": "string", + "example": "2023-11-10T13:10:00+09:00" + }, + "status": { + "type": "string", + "example": "rating" + } + } + }, + "learnsteam_learsteam-quiz-api_internal_models.ExamRequest": { + "type": "object", + "properties": { + "program_id": { + "type": "string", + "example": "7f9329f5-2e36-4638-92d2-73064b7291a4" + }, + "users": { + "type": "array", + "items": { + "type": "integer" + } + } + } + }, + "learnsteam_learsteam-quiz-api_internal_models.ExamResponse": { + "type": "object", + "properties": { + "end_at": { + "type": "string", + "example": "2023-11-10T13:25:00+09:00" + }, + "id": { + "type": "string", + "example": "ef74c59a-c707-4162-a52b-455906c81ec1" + }, + "name": { + "type": "string", + "example": "홍길순" + }, + "program": { + "type": "string", + "example": "파이썬 초급 과정" + }, + "program_id": { + "type": "string", + "example": "7f9329f5-2e36-4638-92d2-73064b7291a4" + }, + "score": { + "type": "integer", + "example": 5 + }, + "start_at": { + "type": "string", + "example": "2023-11-10T13:10:00+09:00" + }, + "status": { + "type": "string", + "example": "ready" + }, + "subject": { + "type": "string", + "example": "출력 Print" + }, + "total": { + "type": "integer", + "example": 5 + }, + "user_id": { + "type": "string", + "example": "f95159dd-c42c-490d-ac6b-ca5d88a266bb" + } + } + }, + "learnsteam_learsteam-quiz-api_internal_models.ExamUpdateRequest": { + "type": "object", + "properties": { + "end_at": { + "type": "string", + "example": "2023-11-10T13:25:00+09:00" + }, + "name": { + "type": "string", + "example": "홍길순" + }, + "program": { + "type": "string", + "example": "파이썬 초급 과정" + }, + "program_id": { + "type": "string", + "example": "7f9329f5-2e36-4638-92d2-73064b7291a4" + }, + "score": { + "type": "integer", + "example": 5 + }, + "start_at": { + "type": "string", + "example": "2023-11-10T13:10:00+09:00" + }, + "status": { + "type": "string", + "example": "ready" + }, + "subject": { + "type": "string", + "example": "출력 Print" + }, + "total": { + "type": "integer", + "example": 5 + }, + "user_id": { + "type": "string", + "example": "f95159dd-c42c-490d-ac6b-ca5d88a266bb" + } + } + }, + "learnsteam_learsteam-quiz-api_internal_models.LoginRequest": { + "type": "object", + "properties": { + "password": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, "learnsteam_learsteam-quiz-api_internal_models.LoginResponse": { "type": "object", "properties": { @@ -834,6 +952,37 @@ const docTemplate = `{ } } }, + "learnsteam_learsteam-quiz-api_internal_models.ProgramRequest": { + "type": "object", + "properties": { + "content": { + "type": "string", + "example": "코스 설명" + }, + "course": { + "type": "string", + "example": "코스 이름" + }, + "publish_at": { + "type": "string", + "example": "2023-11-10T00:00:00+09:00" + }, + "status": { + "type": "string", + "example": "on" + }, + "subject": { + "type": "string", + "example": "프로그램 제목" + }, + "tag": { + "type": "array", + "items": { + "type": "integer" + } + } + } + }, "learnsteam_learsteam-quiz-api_internal_models.ProgramResponse": { "type": "object", "properties": { @@ -945,6 +1094,41 @@ const docTemplate = `{ } } }, + "learnsteam_learsteam-quiz-api_internal_models.QuizRequest": { + "type": "object", + "properties": { + "answer": { + "type": "array", + "items": { + "type": "integer" + } + }, + "choice": { + "type": "array", + "items": { + "type": "integer" + } + }, + "comment": { + "type": "string" + }, + "hint": { + "type": "string" + }, + "program_id": { + "type": "string" + }, + "question": { + "type": "string" + }, + "quiz_type": { + "type": "string" + }, + "sequence": { + "type": "integer" + } + } + }, "learnsteam_learsteam-quiz-api_internal_models.QuizResponse": { "type": "object", "properties": { @@ -990,6 +1174,20 @@ const docTemplate = `{ } } }, + "learnsteam_learsteam-quiz-api_internal_models.RegisterRequest": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "password": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, "learnsteam_learsteam-quiz-api_internal_models.RegisterResponse": { "type": "object", "properties": { diff --git a/docs/swagger.json b/docs/swagger.json index 09871d4..eaf020d 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -12,7 +12,7 @@ "paths": { "/auth/login": { "post": { - "description": "username, password 를 입력하여 로그인", + "description": "사용자가 로그인합니다.", "consumes": [ "application/json" ], @@ -22,24 +22,15 @@ "tags": [ "로그인" ], - "summary": "로그인", + "summary": "사용자 로그인 로그인", "parameters": [ { - "description": "username", - "name": "username", + "description": "Login Body", + "name": "loginBody", "in": "body", "required": true, "schema": { - "type": "string" - } - }, - { - "description": "비밀번호", - "name": "password", - "in": "body", - "required": true, - "schema": { - "type": "string" + "$ref": "#/definitions/learnsteam_learsteam-quiz-api_internal_models.LoginRequest" } } ], @@ -68,30 +59,12 @@ "summary": "회원가입", "parameters": [ { - "description": "username", - "name": "username", + "description": "Register Body", + "name": "registerBody", "in": "body", "required": true, "schema": { - "type": "string" - } - }, - { - "description": "이름", - "name": "name", - "in": "body", - "required": true, - "schema": { - "type": "string" - } - }, - { - "description": "비밀번호", - "name": "password", - "in": "body", - "required": true, - "schema": { - "type": "string" + "$ref": "#/definitions/learnsteam_learsteam-quiz-api_internal_models.RegisterRequest" } } ], @@ -105,6 +78,177 @@ } } }, + "/exam": { + "get": { + "description": "응시 목록을 가져옵니다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Exam" + ], + "summary": "응시 목록 가져오기", + "parameters": [ + { + "type": "string", + "description": "검색어", + "name": "q", + "in": "query" + }, + { + "type": "integer", + "description": "페이지", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "페이지 사이즈", + "name": "limit", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/learnsteam_learsteam-quiz-api_internal_models.ExamListResponse" + } + } + } + }, + "put": { + "description": "퀴즈 프로그램을 수정합니다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Exam" + ], + "summary": "퀴즈 프로그램 수정", + "parameters": [ + { + "description": "Exam Update Body", + "name": "examUpdateBody", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/learnsteam_learsteam-quiz-api_internal_models.ExamUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/learnsteam_learsteam-quiz-api_internal_models.ExamResponse" + } + } + } + }, + "post": { + "description": "응시 매칭을 만듭니다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Exam" + ], + "summary": "응시 매칭 생성", + "parameters": [ + { + "description": "Exam Body", + "name": "examBody", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/learnsteam_learsteam-quiz-api_internal_models.ExamRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/learnsteam_learsteam-quiz-api_internal_models.ExamResponse" + } + } + } + }, + "patch": { + "description": "퀴즈 프로그램 정보를 변경합니다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Exam" + ], + "summary": "퀴즈 프로그램 정보 변경", + "parameters": [ + { + "description": "Exam Patch Body (변경할 필드만 입력)", + "name": "examPatchBody", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/learnsteam_learsteam-quiz-api_internal_models.ExamPatchRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/learnsteam_learsteam-quiz-api_internal_models.ExamResponse" + } + } + } + } + }, + "/exam/{id}": { + "get": { + "description": "ID로 응시 정보를 가져옵니다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Exam" + ], + "summary": "응시(시험) 정보 가져오기", + "parameters": [ + { + "type": "string", + "description": "응시 ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/learnsteam_learsteam-quiz-api_internal_models.ExamResponse" + } + } + } + } + }, "/program": { "get": { "description": "퀴즈 프로그램 목록을 가져옵니다.", @@ -167,60 +311,12 @@ "summary": "퀴즈 프로그램 수정", "parameters": [ { - "description": "프로그램 제목", - "name": "subject", + "description": "Program Body", + "name": "programUpdateBody", "in": "body", "required": true, "schema": { - "type": "string" - } - }, - { - "description": "프로그램 코스", - "name": "course", - "in": "body", - "required": true, - "schema": { - "type": "string" - } - }, - { - "description": "프로그램 내용", - "name": "content", - "in": "body", - "required": true, - "schema": { - "type": "string" - } - }, - { - "description": "프로그램 태그", - "name": "tag", - "in": "body", - "required": true, - "schema": { - "type": "array", - "items": { - "type": "string" - } - } - }, - { - "description": "프로그램 상태 on 또는 off", - "name": "status", - "in": "body", - "required": true, - "schema": { - "type": "string" - } - }, - { - "description": "프로그램 발행 날짜", - "name": "publish_at", - "in": "body", - "required": true, - "schema": { - "type": "string" + "$ref": "#/definitions/learnsteam_learsteam-quiz-api_internal_models.ProgramRequest" } } ], @@ -247,60 +343,12 @@ "summary": "퀴즈 프로그램 생성", "parameters": [ { - "description": "프로그램 제목", - "name": "subject", + "description": "Program Body", + "name": "programBody", "in": "body", "required": true, "schema": { - "type": "string" - } - }, - { - "description": "프로그램 코스", - "name": "course", - "in": "body", - "required": true, - "schema": { - "type": "string" - } - }, - { - "description": "프로그램 내용", - "name": "content", - "in": "body", - "required": true, - "schema": { - "type": "string" - } - }, - { - "description": "프로그램 태그", - "name": "tag", - "in": "body", - "required": true, - "schema": { - "type": "array", - "items": { - "type": "string" - } - } - }, - { - "description": "프로그램 상태 on 또는 off", - "name": "status", - "in": "body", - "required": true, - "schema": { - "type": "string" - } - }, - { - "description": "프로그램 발행 날짜", - "name": "publish_at", - "in": "body", - "required": true, - "schema": { - "type": "string" + "$ref": "#/definitions/learnsteam_learsteam-quiz-api_internal_models.ProgramRequest" } } ], @@ -409,88 +457,12 @@ "summary": "퀴즈 수정", "parameters": [ { - "type": "string", - "description": "퀴즈 ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "프로그램 ID", - "name": "program_id", + "description": "Quiz Body", + "name": "quizBody", "in": "body", "required": true, "schema": { - "type": "string" - } - }, - { - "description": "퀴즈 순서", - "name": "sequence", - "in": "body", - "required": true, - "schema": { - "type": "integer" - } - }, - { - "description": "퀴즈 타입 : choice, check, ox, input", - "name": "quiz_type", - "in": "body", - "required": true, - "schema": { - "type": "string" - } - }, - { - "description": "퀴즈 문제", - "name": "question", - "in": "body", - "required": true, - "schema": { - "type": "string" - } - }, - { - "description": "퀴즈 선택지 : ['선택1','선택2','선택3', '선택4']", - "name": "choice", - "in": "body", - "required": true, - "schema": { - "type": "array", - "items": { - "type": "string" - } - } - }, - { - "description": "퀴즈 정답 : [1,3]", - "name": "answer", - "in": "body", - "required": true, - "schema": { - "type": "array", - "items": { - "type": "string" - } - } - }, - { - "description": "퀴즈 힌트", - "name": "hint", - "in": "body", - "required": true, - "schema": { - "type": "string" - } - }, - { - "description": "퀴즈 해설", - "name": "comment", - "in": "body", - "required": true, - "schema": { - "type": "string" + "$ref": "#/definitions/learnsteam_learsteam-quiz-api_internal_models.QuizRequest" } } ], @@ -517,81 +489,12 @@ "summary": "퀴즈 생성", "parameters": [ { - "description": "프로그램 ID", - "name": "program_id", + "description": "Quiz Body", + "name": "quizBody", "in": "body", "required": true, "schema": { - "type": "string" - } - }, - { - "description": "퀴즈 순서", - "name": "sequence", - "in": "body", - "required": true, - "schema": { - "type": "integer" - } - }, - { - "description": "퀴즈 타입 : choice, check, ox, input", - "name": "quiz_type", - "in": "body", - "required": true, - "schema": { - "type": "string" - } - }, - { - "description": "퀴즈 문제", - "name": "question", - "in": "body", - "required": true, - "schema": { - "type": "string" - } - }, - { - "description": "퀴즈 선택지 : ['선택1','선택2','선택3', '선택4']", - "name": "choice", - "in": "body", - "required": true, - "schema": { - "type": "array", - "items": { - "type": "string" - } - } - }, - { - "description": "퀴즈 정답 : [1,3]", - "name": "answer", - "in": "body", - "required": true, - "schema": { - "type": "array", - "items": { - "type": "string" - } - } - }, - { - "description": "퀴즈 힌트", - "name": "hint", - "in": "body", - "required": true, - "schema": { - "type": "string" - } - }, - { - "description": "퀴즈 해설", - "name": "comment", - "in": "body", - "required": true, - "schema": { - "type": "string" + "$ref": "#/definitions/learnsteam_learsteam-quiz-api_internal_models.QuizRequest" } } ], @@ -748,6 +651,221 @@ } }, "definitions": { + "learnsteam_learsteam-quiz-api_internal_models.Exam": { + "type": "object", + "properties": { + "end_at": { + "type": "string" + }, + "id": { + "type": "string", + "example": "ef74c59a-c707-4162-a52b-455906c81ec1" + }, + "name": { + "type": "string", + "example": "홍길순" + }, + "program": { + "type": "string", + "example": "파이썬 초급 과정" + }, + "program_id": { + "type": "string", + "example": "7f9329f5-2e36-4638-92d2-73064b7291a4" + }, + "score": { + "type": "integer", + "example": 5 + }, + "start_at": { + "type": "string" + }, + "status": { + "type": "string", + "example": "ready" + }, + "subject": { + "type": "string", + "example": "출력 Print" + }, + "total": { + "type": "integer", + "example": 5 + }, + "user_id": { + "type": "string", + "example": "f95159dd-c42c-490d-ac6b-ca5d88a266bb" + } + } + }, + "learnsteam_learsteam-quiz-api_internal_models.ExamListResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/learnsteam_learsteam-quiz-api_internal_models.Exam" + } + }, + "page": { + "type": "integer", + "example": 1 + }, + "pageSize": { + "type": "integer", + "example": 10 + }, + "total": { + "type": "integer", + "example": 999 + }, + "totalPage": { + "type": "integer", + "example": 99 + } + } + }, + "learnsteam_learsteam-quiz-api_internal_models.ExamPatchRequest": { + "type": "object", + "properties": { + "end_at": { + "type": "string", + "example": "2023-11-10T13:25:00+09:00" + }, + "score": { + "type": "integer", + "example": 4 + }, + "start_at": { + "type": "string", + "example": "2023-11-10T13:10:00+09:00" + }, + "status": { + "type": "string", + "example": "rating" + } + } + }, + "learnsteam_learsteam-quiz-api_internal_models.ExamRequest": { + "type": "object", + "properties": { + "program_id": { + "type": "string", + "example": "7f9329f5-2e36-4638-92d2-73064b7291a4" + }, + "users": { + "type": "array", + "items": { + "type": "integer" + } + } + } + }, + "learnsteam_learsteam-quiz-api_internal_models.ExamResponse": { + "type": "object", + "properties": { + "end_at": { + "type": "string", + "example": "2023-11-10T13:25:00+09:00" + }, + "id": { + "type": "string", + "example": "ef74c59a-c707-4162-a52b-455906c81ec1" + }, + "name": { + "type": "string", + "example": "홍길순" + }, + "program": { + "type": "string", + "example": "파이썬 초급 과정" + }, + "program_id": { + "type": "string", + "example": "7f9329f5-2e36-4638-92d2-73064b7291a4" + }, + "score": { + "type": "integer", + "example": 5 + }, + "start_at": { + "type": "string", + "example": "2023-11-10T13:10:00+09:00" + }, + "status": { + "type": "string", + "example": "ready" + }, + "subject": { + "type": "string", + "example": "출력 Print" + }, + "total": { + "type": "integer", + "example": 5 + }, + "user_id": { + "type": "string", + "example": "f95159dd-c42c-490d-ac6b-ca5d88a266bb" + } + } + }, + "learnsteam_learsteam-quiz-api_internal_models.ExamUpdateRequest": { + "type": "object", + "properties": { + "end_at": { + "type": "string", + "example": "2023-11-10T13:25:00+09:00" + }, + "name": { + "type": "string", + "example": "홍길순" + }, + "program": { + "type": "string", + "example": "파이썬 초급 과정" + }, + "program_id": { + "type": "string", + "example": "7f9329f5-2e36-4638-92d2-73064b7291a4" + }, + "score": { + "type": "integer", + "example": 5 + }, + "start_at": { + "type": "string", + "example": "2023-11-10T13:10:00+09:00" + }, + "status": { + "type": "string", + "example": "ready" + }, + "subject": { + "type": "string", + "example": "출력 Print" + }, + "total": { + "type": "integer", + "example": 5 + }, + "user_id": { + "type": "string", + "example": "f95159dd-c42c-490d-ac6b-ca5d88a266bb" + } + } + }, + "learnsteam_learsteam-quiz-api_internal_models.LoginRequest": { + "type": "object", + "properties": { + "password": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, "learnsteam_learsteam-quiz-api_internal_models.LoginResponse": { "type": "object", "properties": { @@ -826,6 +944,37 @@ } } }, + "learnsteam_learsteam-quiz-api_internal_models.ProgramRequest": { + "type": "object", + "properties": { + "content": { + "type": "string", + "example": "코스 설명" + }, + "course": { + "type": "string", + "example": "코스 이름" + }, + "publish_at": { + "type": "string", + "example": "2023-11-10T00:00:00+09:00" + }, + "status": { + "type": "string", + "example": "on" + }, + "subject": { + "type": "string", + "example": "프로그램 제목" + }, + "tag": { + "type": "array", + "items": { + "type": "integer" + } + } + } + }, "learnsteam_learsteam-quiz-api_internal_models.ProgramResponse": { "type": "object", "properties": { @@ -937,6 +1086,41 @@ } } }, + "learnsteam_learsteam-quiz-api_internal_models.QuizRequest": { + "type": "object", + "properties": { + "answer": { + "type": "array", + "items": { + "type": "integer" + } + }, + "choice": { + "type": "array", + "items": { + "type": "integer" + } + }, + "comment": { + "type": "string" + }, + "hint": { + "type": "string" + }, + "program_id": { + "type": "string" + }, + "question": { + "type": "string" + }, + "quiz_type": { + "type": "string" + }, + "sequence": { + "type": "integer" + } + } + }, "learnsteam_learsteam-quiz-api_internal_models.QuizResponse": { "type": "object", "properties": { @@ -982,6 +1166,20 @@ } } }, + "learnsteam_learsteam-quiz-api_internal_models.RegisterRequest": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "password": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, "learnsteam_learsteam-quiz-api_internal_models.RegisterResponse": { "type": "object", "properties": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 843c2d2..de7f912 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -1,4 +1,158 @@ definitions: + learnsteam_learsteam-quiz-api_internal_models.Exam: + properties: + end_at: + type: string + id: + example: ef74c59a-c707-4162-a52b-455906c81ec1 + type: string + name: + example: 홍길순 + type: string + program: + example: 파이썬 초급 과정 + type: string + program_id: + example: 7f9329f5-2e36-4638-92d2-73064b7291a4 + type: string + score: + example: 5 + type: integer + start_at: + type: string + status: + example: ready + type: string + subject: + example: 출력 Print + type: string + total: + example: 5 + type: integer + user_id: + example: f95159dd-c42c-490d-ac6b-ca5d88a266bb + type: string + type: object + learnsteam_learsteam-quiz-api_internal_models.ExamListResponse: + properties: + data: + items: + $ref: '#/definitions/learnsteam_learsteam-quiz-api_internal_models.Exam' + type: array + page: + example: 1 + type: integer + pageSize: + example: 10 + type: integer + total: + example: 999 + type: integer + totalPage: + example: 99 + type: integer + type: object + learnsteam_learsteam-quiz-api_internal_models.ExamPatchRequest: + properties: + end_at: + example: "2023-11-10T13:25:00+09:00" + type: string + score: + example: 4 + type: integer + start_at: + example: "2023-11-10T13:10:00+09:00" + type: string + status: + example: rating + type: string + type: object + learnsteam_learsteam-quiz-api_internal_models.ExamRequest: + properties: + program_id: + example: 7f9329f5-2e36-4638-92d2-73064b7291a4 + type: string + users: + items: + type: integer + type: array + type: object + learnsteam_learsteam-quiz-api_internal_models.ExamResponse: + properties: + end_at: + example: "2023-11-10T13:25:00+09:00" + type: string + id: + example: ef74c59a-c707-4162-a52b-455906c81ec1 + type: string + name: + example: 홍길순 + type: string + program: + example: 파이썬 초급 과정 + type: string + program_id: + example: 7f9329f5-2e36-4638-92d2-73064b7291a4 + type: string + score: + example: 5 + type: integer + start_at: + example: "2023-11-10T13:10:00+09:00" + type: string + status: + example: ready + type: string + subject: + example: 출력 Print + type: string + total: + example: 5 + type: integer + user_id: + example: f95159dd-c42c-490d-ac6b-ca5d88a266bb + type: string + type: object + learnsteam_learsteam-quiz-api_internal_models.ExamUpdateRequest: + properties: + end_at: + example: "2023-11-10T13:25:00+09:00" + type: string + name: + example: 홍길순 + type: string + program: + example: 파이썬 초급 과정 + type: string + program_id: + example: 7f9329f5-2e36-4638-92d2-73064b7291a4 + type: string + score: + example: 5 + type: integer + start_at: + example: "2023-11-10T13:10:00+09:00" + type: string + status: + example: ready + type: string + subject: + example: 출력 Print + type: string + total: + example: 5 + type: integer + user_id: + example: f95159dd-c42c-490d-ac6b-ca5d88a266bb + type: string + type: object + learnsteam_learsteam-quiz-api_internal_models.LoginRequest: + properties: + password: + type: string + username: + type: string + type: object learnsteam_learsteam-quiz-api_internal_models.LoginResponse: properties: refresh_token: @@ -54,6 +208,28 @@ definitions: example: 99 type: integer type: object + learnsteam_learsteam-quiz-api_internal_models.ProgramRequest: + properties: + content: + example: 코스 설명 + type: string + course: + example: 코스 이름 + type: string + publish_at: + example: "2023-11-10T00:00:00+09:00" + type: string + status: + example: "on" + type: string + subject: + example: 프로그램 제목 + type: string + tag: + items: + type: integer + type: array + type: object learnsteam_learsteam-quiz-api_internal_models.ProgramResponse: properties: content: @@ -133,6 +309,29 @@ definitions: example: 1 type: integer type: object + learnsteam_learsteam-quiz-api_internal_models.QuizRequest: + properties: + answer: + items: + type: integer + type: array + choice: + items: + type: integer + type: array + comment: + type: string + hint: + type: string + program_id: + type: string + question: + type: string + quiz_type: + type: string + sequence: + type: integer + type: object learnsteam_learsteam-quiz-api_internal_models.QuizResponse: properties: answer: @@ -165,6 +364,15 @@ definitions: example: 5 type: integer type: object + learnsteam_learsteam-quiz-api_internal_models.RegisterRequest: + properties: + name: + type: string + password: + type: string + username: + type: string + type: object learnsteam_learsteam-quiz-api_internal_models.RegisterResponse: properties: refresh_token: @@ -222,20 +430,14 @@ paths: post: consumes: - application/json - description: username, password 를 입력하여 로그인 + description: 사용자가 로그인합니다. parameters: - - description: username + - description: Login Body in: body - name: username + name: loginBody required: true schema: - type: string - - description: 비밀번호 - in: body - name: password - required: true - schema: - type: string + $ref: '#/definitions/learnsteam_learsteam-quiz-api_internal_models.LoginRequest' produces: - application/json responses: @@ -243,7 +445,7 @@ paths: description: OK schema: $ref: '#/definitions/learnsteam_learsteam-quiz-api_internal_models.LoginResponse' - summary: 로그인 + summary: 사용자 로그인 로그인 tags: - 로그인 /auth/register: @@ -252,24 +454,12 @@ paths: - application/json description: username, name, password 를 입력하여 회원가입 parameters: - - description: username + - description: Register Body in: body - name: username + name: registerBody required: true schema: - type: string - - description: 이름 - in: body - name: name - required: true - schema: - type: string - - description: 비밀번호 - in: body - name: password - required: true - schema: - type: string + $ref: '#/definitions/learnsteam_learsteam-quiz-api_internal_models.RegisterRequest' produces: - application/json responses: @@ -280,6 +470,118 @@ paths: summary: 회원가입 tags: - 회원가입 + /exam: + get: + consumes: + - application/json + description: 응시 목록을 가져옵니다. + parameters: + - description: 검색어 + in: query + name: q + type: string + - description: 페이지 + in: query + name: page + type: integer + - description: 페이지 사이즈 + in: query + name: limit + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/learnsteam_learsteam-quiz-api_internal_models.ExamListResponse' + summary: 응시 목록 가져오기 + tags: + - Exam + patch: + consumes: + - application/json + description: 퀴즈 프로그램 정보를 변경합니다. + parameters: + - description: Exam Patch Body (변경할 필드만 입력) + in: body + name: examPatchBody + required: true + schema: + $ref: '#/definitions/learnsteam_learsteam-quiz-api_internal_models.ExamPatchRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/learnsteam_learsteam-quiz-api_internal_models.ExamResponse' + summary: 퀴즈 프로그램 정보 변경 + tags: + - Exam + post: + consumes: + - application/json + description: 응시 매칭을 만듭니다. + parameters: + - description: Exam Body + in: body + name: examBody + required: true + schema: + $ref: '#/definitions/learnsteam_learsteam-quiz-api_internal_models.ExamRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/learnsteam_learsteam-quiz-api_internal_models.ExamResponse' + summary: 응시 매칭 생성 + tags: + - Exam + put: + consumes: + - application/json + description: 퀴즈 프로그램을 수정합니다. + parameters: + - description: Exam Update Body + in: body + name: examUpdateBody + required: true + schema: + $ref: '#/definitions/learnsteam_learsteam-quiz-api_internal_models.ExamUpdateRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/learnsteam_learsteam-quiz-api_internal_models.ExamResponse' + summary: 퀴즈 프로그램 수정 + tags: + - Exam + /exam/{id}: + get: + consumes: + - application/json + description: ID로 응시 정보를 가져옵니다. + parameters: + - description: 응시 ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/learnsteam_learsteam-quiz-api_internal_models.ExamResponse' + summary: 응시(시험) 정보 가져오기 + tags: + - Exam /program: get: consumes: @@ -317,44 +619,12 @@ paths: - application/json description: 퀴즈 프로그램을 만듭니다. parameters: - - description: 프로그램 제목 + - description: Program Body in: body - name: subject + name: programBody required: true schema: - type: string - - description: 프로그램 코스 - in: body - name: course - required: true - schema: - type: string - - description: 프로그램 내용 - in: body - name: content - required: true - schema: - type: string - - description: 프로그램 태그 - in: body - name: tag - required: true - schema: - items: - type: string - type: array - - description: 프로그램 상태 on 또는 off - in: body - name: status - required: true - schema: - type: string - - description: 프로그램 발행 날짜 - in: body - name: publish_at - required: true - schema: - type: string + $ref: '#/definitions/learnsteam_learsteam-quiz-api_internal_models.ProgramRequest' produces: - application/json responses: @@ -370,44 +640,12 @@ paths: - application/json description: 퀴즈 프로그램을 수정합니다. parameters: - - description: 프로그램 제목 + - description: Program Body in: body - name: subject + name: programUpdateBody required: true schema: - type: string - - description: 프로그램 코스 - in: body - name: course - required: true - schema: - type: string - - description: 프로그램 내용 - in: body - name: content - required: true - schema: - type: string - - description: 프로그램 태그 - in: body - name: tag - required: true - schema: - items: - type: string - type: array - - description: 프로그램 상태 on 또는 off - in: body - name: status - required: true - schema: - type: string - - description: 프로그램 발행 날짜 - in: body - name: publish_at - required: true - schema: - type: string + $ref: '#/definitions/learnsteam_learsteam-quiz-api_internal_models.ProgramRequest' produces: - application/json responses: @@ -477,58 +715,12 @@ paths: - application/json description: 퀴즈를 만듭니다. parameters: - - description: 프로그램 ID + - description: Quiz Body in: body - name: program_id + name: quizBody required: true schema: - type: string - - description: 퀴즈 순서 - in: body - name: sequence - required: true - schema: - type: integer - - description: '퀴즈 타입 : choice, check, ox, input' - in: body - name: quiz_type - required: true - schema: - type: string - - description: 퀴즈 문제 - in: body - name: question - required: true - schema: - type: string - - description: '퀴즈 선택지 : [''선택1'',''선택2'',''선택3'', ''선택4'']' - in: body - name: choice - required: true - schema: - items: - type: string - type: array - - description: '퀴즈 정답 : [1,3]' - in: body - name: answer - required: true - schema: - items: - type: string - type: array - - description: 퀴즈 힌트 - in: body - name: hint - required: true - schema: - type: string - - description: 퀴즈 해설 - in: body - name: comment - required: true - schema: - type: string + $ref: '#/definitions/learnsteam_learsteam-quiz-api_internal_models.QuizRequest' produces: - application/json responses: @@ -544,63 +736,12 @@ paths: - application/json description: 퀴즈를 수정합니다. parameters: - - description: 퀴즈 ID - in: path - name: id - required: true - type: string - - description: 프로그램 ID + - description: Quiz Body in: body - name: program_id + name: quizBody required: true schema: - type: string - - description: 퀴즈 순서 - in: body - name: sequence - required: true - schema: - type: integer - - description: '퀴즈 타입 : choice, check, ox, input' - in: body - name: quiz_type - required: true - schema: - type: string - - description: 퀴즈 문제 - in: body - name: question - required: true - schema: - type: string - - description: '퀴즈 선택지 : [''선택1'',''선택2'',''선택3'', ''선택4'']' - in: body - name: choice - required: true - schema: - items: - type: string - type: array - - description: '퀴즈 정답 : [1,3]' - in: body - name: answer - required: true - schema: - items: - type: string - type: array - - description: 퀴즈 힌트 - in: body - name: hint - required: true - schema: - type: string - - description: 퀴즈 해설 - in: body - name: comment - required: true - schema: - type: string + $ref: '#/definitions/learnsteam_learsteam-quiz-api_internal_models.QuizRequest' produces: - application/json responses: diff --git a/internal/controllers/auth.go b/internal/controllers/auth.go index cf66029..7789399 100644 --- a/internal/controllers/auth.go +++ b/internal/controllers/auth.go @@ -34,9 +34,7 @@ func NewAuthController(service services.AuthService, tokenService services.Token // @Accept json // @Produce json // -// @Param username body string true "username" -// @Param name body string true "이름" -// @Param password body string true "비밀번호" +// @Param registerBody body models.RegisterRequest true "Register Body" // // @Success 200 {object} models.RegisterResponse // @Router /auth/register [post] @@ -69,8 +67,7 @@ func (controller *authController) Register(c *gin.Context) { // @Accept json // @Produce json // -// @Param username body string true "username" -// @Param password body string true "비밀번호" +// @Param loginBody body models.LoginRequest true "Login Body" // // @Success 200 {object} models.LoginResponse // @Router /auth/login [post] diff --git a/internal/controllers/exam.go b/internal/controllers/exam.go new file mode 100644 index 0000000..24cd2b0 --- /dev/null +++ b/internal/controllers/exam.go @@ -0,0 +1,306 @@ +package controllers + +import ( + "encoding/json" + "fmt" + "net/http" + "strconv" + "time" + + "learnsteam/learsteam-quiz-api/internal/models" + "learnsteam/learsteam-quiz-api/internal/services" + + "github.com/gin-gonic/gin" + "github.com/google/uuid" +) + +type ExamController interface { + List(*gin.Context) + Find(*gin.Context) + Create(*gin.Context) + Update(*gin.Context) + Patch(*gin.Context) +} + +type examController struct { + service services.ExamService + userService services.UserService + programService services.ProgramService +} + +func NewExamController(service services.ExamService, userService services.UserService, programService services.ProgramService) ExamController { + return &examController{ + service: service, + userService: userService, + programService: programService, + } +} + +// Exam List +// +// @Summary 응시 목록 가져오기 +// @Description 응시 목록을 가져옵니다. +// @Tags Exam +// +// @Accept json +// @Produce json +// +// @Param q query string false "검색어" +// @Param page query int false "페이지" +// @Param limit query int false "페이지 사이즈" +// +// @Success 200 {object} models.ExamListResponse +// @Router /exam [get] +func (controller *examController) List(c *gin.Context) { + q := c.DefaultQuery("q", "") + page, err := strconv.Atoi(c.DefaultQuery("page", "1")) + if err != nil { + fmt.Println("error : page") + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + limit, err := strconv.Atoi(c.DefaultQuery("limit", "10")) + if err != nil { + fmt.Println("error : limit") + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + result, err := controller.service.List(q, page, limit) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + totalNumber, err := controller.service.Total(q) + if err != nil { + fmt.Println("error : list") + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + totalPage := int64(totalNumber/int64(limit) + 1) + + response := models.ExamListResponse{ + Data: *result, + Total: totalNumber, + Page: page, + TotalPage: totalPage, + PageSize: limit, + } + + c.JSON(http.StatusOK, response) +} + +// Get exam +// +// @Summary 응시(시험) 정보 가져오기 +// @Description ID로 응시 정보를 가져옵니다. +// @Tags Exam +// +// @Accept json +// @Produce json +// +// @Param id path string true "응시 ID" +// +// @Success 200 {object} models.ExamResponse +// @Router /exam/{id} [get] +func (controller *examController) Find(c *gin.Context) { + id := c.Param("id") + + result, err := controller.service.Find(id) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, result) +} + +// Create Exam +// +// @Summary 응시 매칭 생성 +// @Description 응시 매칭을 만듭니다. +// @Tags Exam +// +// @Accept json +// @Produce json +// +// @Param examBody body models.ExamRequest true "Exam Body" +// +// @Success 200 {object} models.ExamResponse +// @Router /exam [post] +func (controller *examController) Create(c *gin.Context) { + var request models.ExamRequest + if err := c.ShouldBindJSON(&request); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + fmt.Println("users", request.Users) + + program, err := controller.programService.Find(request.ProgramID) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + var users []string + err = json.Unmarshal(request.Users, &users) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + count := 0 + for _, user_id := range users { + user, err := controller.userService.Find(user_id) + if err != nil { + continue + } + + exam := &models.Exam{ + ID: uuid.NewString(), + ProgramID: program.ID, + UserID: user_id, + Subject: program.Subject, + Program: program.Course, + Name: user.Name, + Score: 0, + Total: 5, + Status: "ready", + } + + _, err = controller.service.Create(exam) + if err != nil { + continue + } + count += 1 + } + + c.JSON(http.StatusOK, gin.H{"count": count}) +} + +// Update Exam +// +// @Summary 퀴즈 프로그램 수정 +// @Description 퀴즈 프로그램을 수정합니다. +// @Tags Exam +// +// @Accept json +// @Produce json +// +// @Param examUpdateBody body models.ExamUpdateRequest true "Exam Update Body" +// +// @Success 200 {object} models.ExamResponse +// @Router /exam [put] +func (controller *examController) Update(c *gin.Context) { + id := c.Param("id") + var request models.ExamUpdateRequest + if err := c.ShouldBindJSON(&request); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + fmt.Println("request", request) + exam, err := controller.service.Find(id) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + exam.ProgramID = request.ProgramID + exam.UserID = request.UserID + exam.Subject = request.Subject + exam.Program = request.Program + exam.Name = request.Name + exam.Status = request.Status + exam.Score = request.Score + exam.Total = request.Total + + if request.StartAt != "" { + fmt.Println("request.StartAt", request.StartAt) + start_at, err := time.Parse("2006-01-02T15:04:05-07:00", request.StartAt) + if err == nil { + exam.StartAt = &start_at + } + fmt.Println("start_at", start_at) + } + + if request.EndAt != "" { + fmt.Println("request.EndAt", request.EndAt) + end_at, err := time.Parse("2006-01-02T15:04:05-07:00", request.EndAt) + if err == nil { + exam.EndAt = &end_at + } + fmt.Println("end_at", end_at) + } + fmt.Println("exam", exam) + result, err := controller.service.Update(exam) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + fmt.Println("result", result) + c.JSON(http.StatusOK, result) +} + +// Patch Exam +// +// @Summary 퀴즈 프로그램 정보 변경 +// @Description 퀴즈 프로그램 정보를 변경합니다. +// @Tags Exam +// +// @Accept json +// @Produce json +// +// @Param examPatchBody body models.ExamPatchRequest true "Exam Patch Body (변경할 필드만 입력)" +// +// @Success 200 {object} models.ExamResponse +// @Router /exam [patch] +func (controller *examController) Patch(c *gin.Context) { + id := c.Param("id") + var request models.ExamPatchRequest + if err := c.ShouldBindJSON(&request); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + fmt.Println("request", request) + exam, err := controller.service.Find(id) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + if request.Status != "" { + exam.Status = request.Status + } + + if request.StartAt != "" { + fmt.Println("request.StartAt", request.StartAt) + start_at, err := time.Parse("2006-01-02T15:04:05-07:00", request.StartAt) + if err == nil { + exam.StartAt = &start_at + } + fmt.Println("start_at", start_at) + } + + if request.EndAt != "" { + fmt.Println("request.EndAt", request.EndAt) + end_at, err := time.Parse("2006-01-02T15:04:05-07:00", request.EndAt) + if err == nil { + exam.EndAt = &end_at + } + fmt.Println("end_at", end_at) + } + fmt.Println("exam", exam) + result, err := controller.service.Update(exam) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + fmt.Println("result", result) + c.JSON(http.StatusOK, result) +} diff --git a/internal/controllers/program.go b/internal/controllers/program.go index 862ccfa..0084f89 100644 --- a/internal/controllers/program.go +++ b/internal/controllers/program.go @@ -121,12 +121,7 @@ func (controller *programController) Find(c *gin.Context) { // @Accept json // @Produce json // -// @Param subject body string true "프로그램 제목" -// @Param course body string true "프로그램 코스" -// @Param content body string true "프로그램 내용" -// @Param tag body []string true "프로그램 태그" -// @Param status body string true "프로그램 상태 on 또는 off" -// @Param publish_at body string true "프로그램 발행 날짜" +// @Param programBody body models.ProgramRequest true "Program Body" // // @Success 200 {object} models.ProgramResponse // @Router /program [post] @@ -174,12 +169,7 @@ func (controller *programController) Create(c *gin.Context) { // @Accept json // @Produce json // -// @Param subject body string true "프로그램 제목" -// @Param course body string true "프로그램 코스" -// @Param content body string true "프로그램 내용" -// @Param tag body []string true "프로그램 태그" -// @Param status body string true "프로그램 상태 on 또는 off" -// @Param publish_at body string true "프로그램 발행 날짜" +// @Param programUpdateBody body models.ProgramRequest true "Program Body" // // @Success 200 {object} models.ProgramResponse // @Router /program [put] diff --git a/internal/controllers/quiz.go b/internal/controllers/quiz.go index 2c8d7a9..5141fab 100644 --- a/internal/controllers/quiz.go +++ b/internal/controllers/quiz.go @@ -112,14 +112,7 @@ func (controller *quizController) Find(c *gin.Context) { // @Accept json // @Produce json // -// @Param program_id body string true "프로그램 ID" -// @Param sequence body int true "퀴즈 순서" -// @Param quiz_type body string true "퀴즈 타입 : choice, check, ox, input" -// @Param question body string true "퀴즈 문제" -// @Param choice body []string true "퀴즈 선택지 : ['선택1','선택2','선택3', '선택4']" -// @Param answer body []string true "퀴즈 정답 : [1,3]" -// @Param hint body string true "퀴즈 힌트" -// @Param comment body string true "퀴즈 해설" +// @Param quizBody body models.QuizRequest true "Quiz Body" // // @Success 200 {object} models.QuizResponse // @Router /quiz [post] @@ -162,15 +155,7 @@ func (controller *quizController) Create(c *gin.Context) { // @Accept json // @Produce json // -// @Param id path string true "퀴즈 ID" -// @Param program_id body string true "프로그램 ID" -// @Param sequence body int true "퀴즈 순서" -// @Param quiz_type body string true "퀴즈 타입 : choice, check, ox, input" -// @Param question body string true "퀴즈 문제" -// @Param choice body []string true "퀴즈 선택지 : ['선택1','선택2','선택3', '선택4']" -// @Param answer body []string true "퀴즈 정답 : [1,3]" -// @Param hint body string true "퀴즈 힌트" -// @Param comment body string true "퀴즈 해설" +// @Param quizBody body models.QuizRequest true "Quiz Body" // // @Success 200 {object} models.QuizResponse // @Router /quiz [put] diff --git a/internal/database/database.go b/internal/database/database.go index b76eb5e..c9b418d 100644 --- a/internal/database/database.go +++ b/internal/database/database.go @@ -40,5 +40,6 @@ func AutoMigrate() { &models.Token{}, &models.Program{}, &models.Quiz{}, + &models.Exam{}, ) } diff --git a/internal/models/exam.go b/internal/models/exam.go new file mode 100644 index 0000000..d621b5e --- /dev/null +++ b/internal/models/exam.go @@ -0,0 +1,70 @@ +package models + +import ( + "time" + + "gorm.io/datatypes" +) + +type Exam struct { + ID string `json:"id" db:"id" example:"ef74c59a-c707-4162-a52b-455906c81ec1" gorm:"column:id;size:255;primary_key;"` + ProgramID string `json:"program_id" db:"program_id" example:"7f9329f5-2e36-4638-92d2-73064b7291a4" gorm:"column:program_id;size:255;uniqueIndex:idx_exam;"` + UserID string `json:"user_id" db:"user_id" example:"f95159dd-c42c-490d-ac6b-ca5d88a266bb" gorm:"column:user_id;size:255;uniqueIndex:idx_exam;"` + Subject string `json:"subject" db:"subject" example:"출력 Print" gorm:"column:subject;size:255;index;"` + Program string `json:"program" db:"program" example:"파이썬 초급 과정" gorm:"column:program;size:255;index;"` + Name string `json:"name" db:"name" example:"홍길순" gorm:"column:name;size:255;index;"` + Score int `json:"score" db:"score" example:"5" gorm:"column:score;"` + Total int `json:"total" db:"total" example:"5" gorm:"column:total;"` + Status string `json:"status" example:"ready" gorm:"column:status;size:10;index;"` + StartAt *time.Time `json:"start_at" gorm:"column:start_at;index;"` + EndAt *time.Time `json:"end_at" gorm:"column:end_at;index;"` + UpdatedAt time.Time `json:"-" gorm:"column:updated_at;type:DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP;index;->:false"` + CreatedAt time.Time `json:"-" gorm:"column:created_at;type:DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;index;->:false;<-:create"` +} + +type ExamRequest struct { + ProgramID string `json:"program_id" example:"7f9329f5-2e36-4638-92d2-73064b7291a4"` + Users datatypes.JSON `json:"users"` +} + +type ExamResponse struct { + ID string `json:"id" db:"id" example:"ef74c59a-c707-4162-a52b-455906c81ec1"` + ProgramID string `json:"program_id" example:"7f9329f5-2e36-4638-92d2-73064b7291a4"` + UserID string `json:"user_id" example:"f95159dd-c42c-490d-ac6b-ca5d88a266bb"` + Subject string `json:"subject" example:"출력 Print"` + Program string `json:"program" example:"파이썬 초급 과정"` + Name string `json:"name" example:"홍길순"` + Score int `json:"score" example:"5"` + Total int `json:"total" example:"5"` + Status string `json:"status" example:"ready"` + StartAt string `json:"start_at,omitempty" example:"2023-11-10T13:10:00+09:00"` + EndAt string `json:"end_at,omitempty" example:"2023-11-10T13:25:00+09:00"` +} + +type ExamUpdateRequest struct { + ProgramID string `json:"program_id" example:"7f9329f5-2e36-4638-92d2-73064b7291a4"` + UserID string `json:"user_id" example:"f95159dd-c42c-490d-ac6b-ca5d88a266bb"` + Subject string `json:"subject" example:"출력 Print"` + Program string `json:"program" example:"파이썬 초급 과정"` + Name string `json:"name" example:"홍길순"` + Score int `json:"score" example:"5"` + Total int `json:"total" example:"5"` + Status string `json:"status" example:"ready"` + StartAt string `json:"start_at,omitempty" example:"2023-11-10T13:10:00+09:00"` + EndAt string `json:"end_at,omitempty" example:"2023-11-10T13:25:00+09:00"` +} + +type ExamPatchRequest struct { + Score int `json:"score,omitempty" example:"4"` + Status string `json:"status,omitempty" example:"rating"` + StartAt string `json:"start_at,omitempty" example:"2023-11-10T13:10:00+09:00"` + EndAt string `json:"end_at,omitempty" example:"2023-11-10T13:25:00+09:00"` +} + +type ExamListResponse struct { + Data []Exam `json:"data"` + Total int64 `json:"total" example:"999"` + Page int `json:"page" example:"1"` + TotalPage int64 `json:"totalPage" example:"99"` + PageSize int `json:"pageSize" example:"10"` +} diff --git a/internal/models/program.go b/internal/models/program.go index c162a59..a45ba16 100644 --- a/internal/models/program.go +++ b/internal/models/program.go @@ -15,8 +15,8 @@ type Program struct { Tags string `json:"-" db:"tags" gorm:"column:tags;index;"` Status string `json:"status" example:"on" gorm:"column:status;size:10;index;"` PublishAt time.Time `json:"publish_at" example:"2023-11-10T00:00:00+09:00" gorm:"column:publish_at;index;"` - UpdatedAt time.Time `json:"-" gorm:"column:updated_at;index;"` - CreatedAt time.Time `json:"-" gorm:"column:created_at;index;"` + UpdatedAt time.Time `json:"-" gorm:"column:updated_at;type:DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP;index;->:false"` + CreatedAt time.Time `json:"-" gorm:"column:created_at;type:DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;index;->:false;<-:create"` } type ProgramRequest struct { diff --git a/internal/models/quiz.go b/internal/models/quiz.go index f22fdff..3cead79 100644 --- a/internal/models/quiz.go +++ b/internal/models/quiz.go @@ -16,8 +16,8 @@ type Quiz struct { Answer datatypes.JSON `json:"answer" db:"answer" gorm:"column:answer;"` Hint string `json:"hint" db:"hint" example:"퀴즈 힌트" gorm:"column:answer;size:1024;"` Comment string `json:"comment" db:"comment" example:"퀴즈 해설" gorm:"column:comment;size:1024;"` - UpdatedAt time.Time `json:"-" gorm:"column:updated_at;index;"` - CreatedAt time.Time `json:"-" gorm:"column:created_at;index;"` + UpdatedAt time.Time `json:"-" gorm:"column:updated_at;type:DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP;index;->:false"` + CreatedAt time.Time `json:"-" gorm:"column:created_at;type:DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;index;->:false;<-:create"` } type QuizRequest struct { diff --git a/internal/models/user.go b/internal/models/user.go index 2b386f3..4e5f887 100644 --- a/internal/models/user.go +++ b/internal/models/user.go @@ -8,8 +8,8 @@ type User struct { Name string `json:"name" db:"name" example:"홍길동" gorm:"column:name;size:50;"` Score int32 `json:"score" db:"score" example:"9999" gorm:"column:score;"` Password string `json:"-" db:"password" gorm:"column:password;size:255;not null;"` - UpdatedAt time.Time `json:"-" gorm:"column:updated_at;index;"` - CreatedAt time.Time `json:"-" gorm:"column:created_at;index;"` + UpdatedAt time.Time `json:"-" gorm:"column:updated_at;type:DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP;index;->:false"` + CreatedAt time.Time `json:"-" gorm:"column:created_at;type:DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;index;->:false;<-:create"` } type UserListResponse struct { diff --git a/internal/repositories/exam.go b/internal/repositories/exam.go new file mode 100644 index 0000000..38f5d0b --- /dev/null +++ b/internal/repositories/exam.go @@ -0,0 +1,81 @@ +package repositories + +import ( + "learnsteam/learsteam-quiz-api/internal/models" + + "gorm.io/gorm" +) + +type examRepository struct { + DB *gorm.DB +} + +func NewExamRepository(db *gorm.DB) ExamRepository { + return &examRepository{ + DB: db, + } +} + +type ExamRepository interface { + List(string, int, int) (*[]models.Exam, error) + Total(string) (int64, error) + + Find(string) (*models.Exam, error) + Create(*models.Exam) (*models.Exam, error) + Update(*models.Exam) (*models.Exam, error) + Delete(string) error +} + +func (r *examRepository) List(q string, page int, limit int) (*[]models.Exam, error) { + var exams *[]models.Exam + var err error + offset := limit * (page - 1) + if q != "" { + err = r.DB.Offset(offset).Limit(limit).Order("created_at DESC").Where("subject LIKE ? OR program LIKE ? OR name LIKE ?", "%"+q+"%", "%"+q+"%", "%"+q+"%").Find(&exams).Error + } else { + err = r.DB.Offset(offset).Limit(limit).Order("created_at DESC").Find(&exams).Error + } + return exams, err +} + +func (r *examRepository) Total(q string) (int64, error) { + var total int64 + var err error + if q != "" { + err = r.DB.Model(&models.Exam{}).Where("subject LIKE ? OR program LIKE ? OR name LIKE ?", "%"+q+"%", "%"+q+"%", "%"+q+"%").Count(&total).Error + } else { + err = r.DB.Model(&models.Exam{}).Count(&total).Error + } + + return total, err +} + +func (r *examRepository) Find(id string) (*models.Exam, error) { + var exam *models.Exam + err := r.DB.Where("id = ?", id).First(&exam).Error + return exam, err +} + +func (r *examRepository) Create(exam *models.Exam) (*models.Exam, error) { + err := r.DB.Create(&exam).Error + return exam, err +} + +func (r *examRepository) Update(exam *models.Exam) (*models.Exam, error) { + var row *models.Exam + if err := r.DB.Where("id=?", exam.ID).First(&row).Error; err != nil { + return nil, err + } + + err := r.DB.Model(&row).Select("*").Updates(&exam).Error + return row, err +} + +func (r *examRepository) Delete(id string) error { + var exam *models.Exam + if err := r.DB.Where("id=?", id).First(&exam).Error; err != nil { + return err + } + err := r.DB.Delete(&exam).Error + return err +} diff --git a/internal/routers/exam.go b/internal/routers/exam.go new file mode 100644 index 0000000..442a3e9 --- /dev/null +++ b/internal/routers/exam.go @@ -0,0 +1,65 @@ +package routers + +import ( + "learnsteam/learsteam-quiz-api/internal/controllers" + "learnsteam/learsteam-quiz-api/internal/middleware" + "learnsteam/learsteam-quiz-api/internal/repositories" + "learnsteam/learsteam-quiz-api/internal/services" + + "github.com/gin-gonic/gin" + "gorm.io/gorm" +) + +func InitExamRouter(db *gorm.DB, router *gin.Engine) { + r := NewExamRouter(db, router) + r.SetExamRouter(db, router) +} + +type ExamRouter interface { + SetRouter(db *gorm.DB, router *gin.Engine) +} + +type examRouter struct { + db *gorm.DB + repository repositories.ExamRepository + programRepository repositories.ProgramRepository + userRepository repositories.UserRepository + + service services.ExamService + programService services.ProgramService + userService services.UserService + + controller controllers.ExamController + + router *gin.Engine +} + +func NewExamRouter(db *gorm.DB, router *gin.Engine) *examRouter { + repository := repositories.NewExamRepository(db) + programRepository := repositories.NewProgramRepository(db) + tokenRepository := repositories.NewTokenRepository(db) + userRepository := repositories.NewUserRepository(db) + + service := services.NewExamService(repository) + programService := services.NewProgramService(programRepository) + userService := services.NewUserService(userRepository, tokenRepository) + + controller := controllers.NewExamController(service, userService, programService) + + return &examRouter{ + db: db, + repository: repository, + service: service, + controller: controller, + router: router, + } +} + +func (r *examRouter) SetExamRouter(db *gorm.DB, router *gin.Engine) { + group := router.Group("/exam") + group.GET("", middleware.Auth(), r.controller.List) + group.GET("/:id", middleware.Auth(), r.controller.Find) + group.POST("", middleware.Auth(), r.controller.Create) + group.PUT("/:id", middleware.Auth(), r.controller.Update) + group.PATCH("/:id", middleware.Auth(), r.controller.Patch) +} diff --git a/internal/routers/router.go b/internal/routers/router.go index 98dff02..31830b6 100644 --- a/internal/routers/router.go +++ b/internal/routers/router.go @@ -25,6 +25,7 @@ func Init() { InitUserRouter(maindb, Router) InitProgramRouter(maindb, Router) InitQuizRouter(maindb, Router) + InitExamRouter(maindb, Router) InitSwaggerRouter(Router) } diff --git a/internal/services/exam.go b/internal/services/exam.go new file mode 100644 index 0000000..2d56b62 --- /dev/null +++ b/internal/services/exam.go @@ -0,0 +1,56 @@ +package services + +import ( + "learnsteam/learsteam-quiz-api/internal/models" + "learnsteam/learsteam-quiz-api/internal/repositories" +) + +type examService struct { + repository repositories.ExamRepository +} + +type ExamService interface { + List(string, int, int) (*[]models.Exam, error) + Total(string) (int64, error) + + Find(string) (*models.Exam, error) + Create(*models.Exam) (*models.Exam, error) + Update(*models.Exam) (*models.Exam, error) + Delete(string) error +} + +func NewExamService(repository repositories.ExamRepository) ExamService { + return &examService{ + repository: repository, + } +} + +func (s *examService) List(q string, page int, limit int) (*[]models.Exam, error) { + return s.repository.List(q, page, limit) +} + +func (s *examService) Total(q string) (int64, error) { + return s.repository.Total(q) +} + +func (s *examService) Find(id string) (*models.Exam, error) { + return s.repository.Find(id) +} + +func (s *examService) Create(exam *models.Exam) (*models.Exam, error) { + result, err := s.repository.Create(exam) + + return result, err +} + +func (s *examService) Update(exam *models.Exam) (*models.Exam, error) { + result, err := s.repository.Update(exam) + + return result, err +} + +func (s *examService) Delete(id string) error { + err := s.repository.Delete(id) + + return err +}