퀴즈 풀기/점수

This commit is contained in:
JongYeob Sheen 2024-02-19 22:43:31 +09:00
parent 41e1ed5c58
commit 05292be591
24 changed files with 2425 additions and 39 deletions

15
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,15 @@
{
// IntelliSense .
// .
// https://go.microsoft.com/fwlink/?linkid=830387() .
"version": "0.2.0",
"configurations": [
{
"name": "Launch Package",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${fileDirname}"
}
]
}

View File

@ -273,6 +273,304 @@ const docTemplate = `{
}
}
},
"/paper": {
"get": {
"security": [
{
"Bearer": []
}
],
"description": "목록을 가져옵니다.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"페이퍼"
],
"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_cslms-api_internal_models.PaperListResponse"
}
}
}
}
},
"/paper/{paper_id}": {
"get": {
"security": [
{
"Bearer": []
}
],
"description": "ID로 페이퍼 정보를 가져옵니다.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"페이퍼"
],
"summary": "페이퍼 정보 가져오기",
"parameters": [
{
"type": "integer",
"description": "Paper ID",
"name": "paper_id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/learnsteam_cslms-api_internal_models.PaperResponse"
}
}
}
}
},
"/paper/{paper_id}/finish": {
"patch": {
"security": [
{
"Bearer": []
}
],
"description": "퀴즈 종료합니다.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"페이퍼"
],
"summary": "퀴즈 종료",
"parameters": [
{
"type": "integer",
"description": "Paper ID",
"name": "paper_id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/learnsteam_cslms-api_internal_models.Paper"
}
}
}
}
},
"/paper/{paper_id}/quiz": {
"get": {
"security": [
{
"Bearer": []
}
],
"description": "퀴즈페이퍼 퀴즈 목록을 가져옵니다.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"페이퍼"
],
"summary": "퀴즈페이퍼 퀴즈 목록 가져오기",
"parameters": [
{
"type": "integer",
"description": "퀴즈페이퍼 ID",
"name": "paper_id",
"in": "path",
"required": true
},
{
"type": "integer",
"description": "페이지",
"name": "page",
"in": "query"
},
{
"type": "integer",
"description": "페이지 사이즈",
"name": "limit",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/learnsteam_cslms-api_internal_models.UserQuizListResponse"
}
}
}
}
},
"/paper/{paper_id}/quiz/{quiz_id}": {
"get": {
"security": [
{
"Bearer": []
}
],
"description": "ID로 퀴즈를 가져옵니다.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"페이퍼"
],
"summary": "퀴즈페이퍼 퀴즈 가져오기",
"parameters": [
{
"type": "string",
"description": "Paper ID",
"name": "paper_id",
"in": "path",
"required": true
},
{
"type": "string",
"description": "사용자퀴즈 ID",
"name": "quiz_id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/learnsteam_cslms-api_internal_models.UserQuizResponse"
}
}
}
}
},
"/paper/{paper_id}/quiz/{quiz_id}/answer": {
"patch": {
"security": [
{
"Bearer": []
}
],
"description": "퀴즈 답변을 제출합니다..",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"페이퍼"
],
"summary": "퀴즈 답변",
"parameters": [
{
"type": "integer",
"description": "Paper ID",
"name": "paper_id",
"in": "path",
"required": true
},
{
"type": "integer",
"description": "Quiz ID",
"name": "quiz_id",
"in": "path",
"required": true
},
{
"description": "Answer Body",
"name": "quizBody",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/learnsteam_cslms-api_internal_models.AnswerQuizRequest"
}
}
],
"responses": {}
}
},
"/paper/{paper_id}/start": {
"patch": {
"security": [
{
"Bearer": []
}
],
"description": "퀴즈 시작합니다.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"페이퍼"
],
"summary": "퀴즈 시작",
"parameters": [
{
"type": "integer",
"description": "Paper ID",
"name": "paper_id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/learnsteam_cslms-api_internal_models.Paper"
}
}
}
}
},
"/quiz": {
"get": {
"security": [
@ -982,6 +1280,43 @@ const docTemplate = `{
}
}
}
},
"patch": {
"security": [
{
"Bearer": []
}
],
"description": "사용자퀴즈 답변을 제출합니다..",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"사용자퀴즈"
],
"summary": "사용자퀴즈 답변",
"parameters": [
{
"type": "integer",
"description": "Quiz ID",
"name": "id",
"in": "path",
"required": true
},
{
"description": "Answer Body",
"name": "quizBody",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/learnsteam_cslms-api_internal_models.AnswerQuizRequest"
}
}
],
"responses": {}
}
},
"/userquizpaper": {
@ -1199,6 +1534,21 @@ const docTemplate = `{
}
},
"definitions": {
"learnsteam_cslms-api_internal_models.AnswerQuizRequest": {
"type": "object",
"properties": {
"answer": {
"type": "array",
"items": {
"type": "string"
},
"example": [
"option1",
"option4"
]
}
}
},
"learnsteam_cslms-api_internal_models.Center": {
"type": "object",
"properties": {
@ -1329,6 +1679,138 @@ const docTemplate = `{
}
}
},
"learnsteam_cslms-api_internal_models.Paper": {
"type": "object",
"properties": {
"center_id": {
"type": "integer",
"example": 1000015
},
"done_at": {
"type": "string"
},
"guid_id": {
"type": "string",
"example": "7f9329f5-2e36-4638-92d2-73064b7291a4"
},
"id": {
"type": "integer",
"example": 1000015
},
"quiz_paper_id": {
"type": "integer",
"example": 1000001
},
"start_at": {
"type": "string"
},
"status": {
"type": "string",
"example": "wating"
},
"total_score": {
"type": "number",
"example": 100
},
"user_id": {
"type": "integer",
"example": 1000002
},
"user_score": {
"type": "number",
"example": 5
}
}
},
"learnsteam_cslms-api_internal_models.PaperListResponse": {
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"$ref": "#/definitions/learnsteam_cslms-api_internal_models.PaperResponse"
}
},
"page": {
"type": "integer",
"example": 1
},
"pageSize": {
"type": "integer",
"example": 10
},
"total": {
"type": "integer",
"example": 999
},
"totalPage": {
"type": "integer",
"example": 99
}
}
},
"learnsteam_cslms-api_internal_models.PaperResponse": {
"type": "object",
"properties": {
"category": {
"type": "string",
"example": "파이썬기본"
},
"center_id": {
"type": "integer",
"example": 1000015
},
"created_at": {
"type": "string"
},
"done_at": {
"type": "string"
},
"first_name": {
"type": "string",
"example": "길순"
},
"guid_id": {
"type": "string",
"example": "7f9329f5-2e36-4638-92d2-73064b7291a4"
},
"id": {
"type": "integer",
"example": 1000015
},
"last_name": {
"type": "string",
"example": "홍"
},
"quiz_paper_id": {
"type": "integer",
"example": 1000001
},
"start_at": {
"type": "string"
},
"status": {
"type": "string",
"example": "wating"
},
"title": {
"type": "string",
"example": "출력 Print"
},
"total_score": {
"type": "number",
"example": 100
},
"user_id": {
"type": "integer",
"example": 1000002
},
"user_score": {
"type": "number",
"example": 5
}
}
},
"learnsteam_cslms-api_internal_models.Quiz": {
"type": "object",
"properties": {

View File

@ -265,6 +265,304 @@
}
}
},
"/paper": {
"get": {
"security": [
{
"Bearer": []
}
],
"description": "목록을 가져옵니다.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"페이퍼"
],
"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_cslms-api_internal_models.PaperListResponse"
}
}
}
}
},
"/paper/{paper_id}": {
"get": {
"security": [
{
"Bearer": []
}
],
"description": "ID로 페이퍼 정보를 가져옵니다.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"페이퍼"
],
"summary": "페이퍼 정보 가져오기",
"parameters": [
{
"type": "integer",
"description": "Paper ID",
"name": "paper_id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/learnsteam_cslms-api_internal_models.PaperResponse"
}
}
}
}
},
"/paper/{paper_id}/finish": {
"patch": {
"security": [
{
"Bearer": []
}
],
"description": "퀴즈 종료합니다.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"페이퍼"
],
"summary": "퀴즈 종료",
"parameters": [
{
"type": "integer",
"description": "Paper ID",
"name": "paper_id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/learnsteam_cslms-api_internal_models.Paper"
}
}
}
}
},
"/paper/{paper_id}/quiz": {
"get": {
"security": [
{
"Bearer": []
}
],
"description": "퀴즈페이퍼 퀴즈 목록을 가져옵니다.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"페이퍼"
],
"summary": "퀴즈페이퍼 퀴즈 목록 가져오기",
"parameters": [
{
"type": "integer",
"description": "퀴즈페이퍼 ID",
"name": "paper_id",
"in": "path",
"required": true
},
{
"type": "integer",
"description": "페이지",
"name": "page",
"in": "query"
},
{
"type": "integer",
"description": "페이지 사이즈",
"name": "limit",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/learnsteam_cslms-api_internal_models.UserQuizListResponse"
}
}
}
}
},
"/paper/{paper_id}/quiz/{quiz_id}": {
"get": {
"security": [
{
"Bearer": []
}
],
"description": "ID로 퀴즈를 가져옵니다.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"페이퍼"
],
"summary": "퀴즈페이퍼 퀴즈 가져오기",
"parameters": [
{
"type": "string",
"description": "Paper ID",
"name": "paper_id",
"in": "path",
"required": true
},
{
"type": "string",
"description": "사용자퀴즈 ID",
"name": "quiz_id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/learnsteam_cslms-api_internal_models.UserQuizResponse"
}
}
}
}
},
"/paper/{paper_id}/quiz/{quiz_id}/answer": {
"patch": {
"security": [
{
"Bearer": []
}
],
"description": "퀴즈 답변을 제출합니다..",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"페이퍼"
],
"summary": "퀴즈 답변",
"parameters": [
{
"type": "integer",
"description": "Paper ID",
"name": "paper_id",
"in": "path",
"required": true
},
{
"type": "integer",
"description": "Quiz ID",
"name": "quiz_id",
"in": "path",
"required": true
},
{
"description": "Answer Body",
"name": "quizBody",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/learnsteam_cslms-api_internal_models.AnswerQuizRequest"
}
}
],
"responses": {}
}
},
"/paper/{paper_id}/start": {
"patch": {
"security": [
{
"Bearer": []
}
],
"description": "퀴즈 시작합니다.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"페이퍼"
],
"summary": "퀴즈 시작",
"parameters": [
{
"type": "integer",
"description": "Paper ID",
"name": "paper_id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/learnsteam_cslms-api_internal_models.Paper"
}
}
}
}
},
"/quiz": {
"get": {
"security": [
@ -974,6 +1272,43 @@
}
}
}
},
"patch": {
"security": [
{
"Bearer": []
}
],
"description": "사용자퀴즈 답변을 제출합니다..",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"사용자퀴즈"
],
"summary": "사용자퀴즈 답변",
"parameters": [
{
"type": "integer",
"description": "Quiz ID",
"name": "id",
"in": "path",
"required": true
},
{
"description": "Answer Body",
"name": "quizBody",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/learnsteam_cslms-api_internal_models.AnswerQuizRequest"
}
}
],
"responses": {}
}
},
"/userquizpaper": {
@ -1191,6 +1526,21 @@
}
},
"definitions": {
"learnsteam_cslms-api_internal_models.AnswerQuizRequest": {
"type": "object",
"properties": {
"answer": {
"type": "array",
"items": {
"type": "string"
},
"example": [
"option1",
"option4"
]
}
}
},
"learnsteam_cslms-api_internal_models.Center": {
"type": "object",
"properties": {
@ -1321,6 +1671,138 @@
}
}
},
"learnsteam_cslms-api_internal_models.Paper": {
"type": "object",
"properties": {
"center_id": {
"type": "integer",
"example": 1000015
},
"done_at": {
"type": "string"
},
"guid_id": {
"type": "string",
"example": "7f9329f5-2e36-4638-92d2-73064b7291a4"
},
"id": {
"type": "integer",
"example": 1000015
},
"quiz_paper_id": {
"type": "integer",
"example": 1000001
},
"start_at": {
"type": "string"
},
"status": {
"type": "string",
"example": "wating"
},
"total_score": {
"type": "number",
"example": 100
},
"user_id": {
"type": "integer",
"example": 1000002
},
"user_score": {
"type": "number",
"example": 5
}
}
},
"learnsteam_cslms-api_internal_models.PaperListResponse": {
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"$ref": "#/definitions/learnsteam_cslms-api_internal_models.PaperResponse"
}
},
"page": {
"type": "integer",
"example": 1
},
"pageSize": {
"type": "integer",
"example": 10
},
"total": {
"type": "integer",
"example": 999
},
"totalPage": {
"type": "integer",
"example": 99
}
}
},
"learnsteam_cslms-api_internal_models.PaperResponse": {
"type": "object",
"properties": {
"category": {
"type": "string",
"example": "파이썬기본"
},
"center_id": {
"type": "integer",
"example": 1000015
},
"created_at": {
"type": "string"
},
"done_at": {
"type": "string"
},
"first_name": {
"type": "string",
"example": "길순"
},
"guid_id": {
"type": "string",
"example": "7f9329f5-2e36-4638-92d2-73064b7291a4"
},
"id": {
"type": "integer",
"example": 1000015
},
"last_name": {
"type": "string",
"example": "홍"
},
"quiz_paper_id": {
"type": "integer",
"example": 1000001
},
"start_at": {
"type": "string"
},
"status": {
"type": "string",
"example": "wating"
},
"title": {
"type": "string",
"example": "출력 Print"
},
"total_score": {
"type": "number",
"example": 100
},
"user_id": {
"type": "integer",
"example": 1000002
},
"user_score": {
"type": "number",
"example": 5
}
}
},
"learnsteam_cslms-api_internal_models.Quiz": {
"type": "object",
"properties": {

View File

@ -1,4 +1,14 @@
definitions:
learnsteam_cslms-api_internal_models.AnswerQuizRequest:
properties:
answer:
example:
- option1
- option4
items:
type: string
type: array
type: object
learnsteam_cslms-api_internal_models.Center:
properties:
center_name:
@ -92,6 +102,101 @@ definitions:
user:
$ref: '#/definitions/learnsteam_cslms-api_internal_models.User'
type: object
learnsteam_cslms-api_internal_models.Paper:
properties:
center_id:
example: 1000015
type: integer
done_at:
type: string
guid_id:
example: 7f9329f5-2e36-4638-92d2-73064b7291a4
type: string
id:
example: 1000015
type: integer
quiz_paper_id:
example: 1000001
type: integer
start_at:
type: string
status:
example: wating
type: string
total_score:
example: 100
type: number
user_id:
example: 1000002
type: integer
user_score:
example: 5
type: number
type: object
learnsteam_cslms-api_internal_models.PaperListResponse:
properties:
data:
items:
$ref: '#/definitions/learnsteam_cslms-api_internal_models.PaperResponse'
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_cslms-api_internal_models.PaperResponse:
properties:
category:
example: 파이썬기본
type: string
center_id:
example: 1000015
type: integer
created_at:
type: string
done_at:
type: string
first_name:
example: 길순
type: string
guid_id:
example: 7f9329f5-2e36-4638-92d2-73064b7291a4
type: string
id:
example: 1000015
type: integer
last_name:
example:
type: string
quiz_paper_id:
example: 1000001
type: integer
start_at:
type: string
status:
example: wating
type: string
title:
example: 출력 Print
type: string
total_score:
example: 100
type: number
user_id:
example: 1000002
type: integer
user_score:
example: 5
type: number
type: object
learnsteam_cslms-api_internal_models.Quiz:
properties:
answer:
@ -886,6 +991,194 @@ paths:
summary: 센터 수정
tags:
- 센터
/paper:
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_cslms-api_internal_models.PaperListResponse'
security:
- Bearer: []
summary: 목록 가져오기
tags:
- 페이퍼
/paper/{paper_id}:
get:
consumes:
- application/json
description: ID로 페이퍼 정보를 가져옵니다.
parameters:
- description: Paper ID
in: path
name: paper_id
required: true
type: integer
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/learnsteam_cslms-api_internal_models.PaperResponse'
security:
- Bearer: []
summary: 페이퍼 정보 가져오기
tags:
- 페이퍼
/paper/{paper_id}/finish:
patch:
consumes:
- application/json
description: 퀴즈 종료합니다.
parameters:
- description: Paper ID
in: path
name: paper_id
required: true
type: integer
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/learnsteam_cslms-api_internal_models.Paper'
security:
- Bearer: []
summary: 퀴즈 종료
tags:
- 페이퍼
/paper/{paper_id}/quiz:
get:
consumes:
- application/json
description: 퀴즈페이퍼 퀴즈 목록을 가져옵니다.
parameters:
- description: 퀴즈페이퍼 ID
in: path
name: paper_id
required: true
type: integer
- 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_cslms-api_internal_models.UserQuizListResponse'
security:
- Bearer: []
summary: 퀴즈페이퍼 퀴즈 목록 가져오기
tags:
- 페이퍼
/paper/{paper_id}/quiz/{quiz_id}:
get:
consumes:
- application/json
description: ID로 퀴즈를 가져옵니다.
parameters:
- description: Paper ID
in: path
name: paper_id
required: true
type: string
- description: 사용자퀴즈 ID
in: path
name: quiz_id
required: true
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/learnsteam_cslms-api_internal_models.UserQuizResponse'
security:
- Bearer: []
summary: 퀴즈페이퍼 퀴즈 가져오기
tags:
- 페이퍼
/paper/{paper_id}/quiz/{quiz_id}/answer:
patch:
consumes:
- application/json
description: 퀴즈 답변을 제출합니다..
parameters:
- description: Paper ID
in: path
name: paper_id
required: true
type: integer
- description: Quiz ID
in: path
name: quiz_id
required: true
type: integer
- description: Answer Body
in: body
name: quizBody
required: true
schema:
$ref: '#/definitions/learnsteam_cslms-api_internal_models.AnswerQuizRequest'
produces:
- application/json
responses: {}
security:
- Bearer: []
summary: 퀴즈 답변
tags:
- 페이퍼
/paper/{paper_id}/start:
patch:
consumes:
- application/json
description: 퀴즈 시작합니다.
parameters:
- description: Paper ID
in: path
name: paper_id
required: true
type: integer
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/learnsteam_cslms-api_internal_models.Paper'
security:
- Bearer: []
summary: 퀴즈 시작
tags:
- 페이퍼
/quiz:
get:
consumes:
@ -1306,6 +1599,30 @@ paths:
summary: 사용자퀴즈 가져오기
tags:
- 사용자퀴즈
patch:
consumes:
- application/json
description: 사용자퀴즈 답변을 제출합니다..
parameters:
- description: Quiz ID
in: path
name: id
required: true
type: integer
- description: Answer Body
in: body
name: quizBody
required: true
schema:
$ref: '#/definitions/learnsteam_cslms-api_internal_models.AnswerQuizRequest'
produces:
- application/json
responses: {}
security:
- Bearer: []
summary: 사용자퀴즈 답변
tags:
- 사용자퀴즈
put:
consumes:
- application/json

6
go.mod
View File

@ -1,15 +1,17 @@
module learnsteam/cslms-api
go 1.21
go 1.22
require (
github.com/apex/gateway v1.1.2
github.com/gin-contrib/cors v1.4.0
github.com/golang-jwt/jwt/v5 v5.1.0
github.com/google/go-cmp v0.6.0
github.com/google/uuid v1.4.0
github.com/stretchr/testify v1.8.4
github.com/swaggo/files v1.0.1
github.com/swaggo/gin-swagger v1.6.0
github.com/swaggo/swag v1.16.2
golang.org/x/crypto v0.15.0
gorm.io/datatypes v1.2.0
gorm.io/driver/mysql v1.5.2
@ -34,7 +36,6 @@ require (
github.com/go-playground/validator/v10 v10.16.0 // indirect
github.com/go-sql-driver/mysql v1.7.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/josharian/intern v1.0.0 // indirect
@ -48,7 +49,6 @@ require (
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/swaggo/swag v1.16.2 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect
golang.org/x/arch v0.6.0 // indirect

4
go.sum
View File

@ -72,8 +72,8 @@ github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=

View File

@ -0,0 +1,426 @@
package controllers
import (
"encoding/json"
"fmt"
"net/http"
"strconv"
"time"
"learnsteam/cslms-api/internal/models"
"learnsteam/cslms-api/internal/services"
"github.com/gin-gonic/gin"
"github.com/google/go-cmp/cmp"
"gorm.io/datatypes"
)
type PaperController interface {
List(*gin.Context)
Find(*gin.Context)
Start(*gin.Context)
Finish(*gin.Context)
QuizList(*gin.Context)
QuizFind(*gin.Context)
QuizAnswer(*gin.Context)
}
type paperController struct {
service services.PaperService
paperQuizService services.PaperQuizService
userService services.UserService
quizService services.QuizService
quizPaperService services.QuizPaperService
userQuizService services.UserQuizService
}
func NewPaperController(
service services.PaperService,
paperQuizService services.PaperQuizService,
quizService services.QuizService,
quizPaperService services.QuizPaperService,
userService services.UserService,
userQuizServie services.UserQuizService,
) PaperController {
return &paperController{
service: service,
paperQuizService: paperQuizService,
userService: userService,
quizService: quizService,
quizPaperService: quizPaperService,
userQuizService: userQuizServie,
}
}
// Paper List
//
// @Summary 목록 가져오기
// @Description 목록을 가져옵니다.
// @Tags 페이퍼
//
// @Accept json
// @Produce json
//
// @Security Bearer
//
// @Param q query string false "검색어"
// @Param page query int false "페이지"
// @Param limit query int false "페이지 사이즈"
//
// @Success 200 {object} models.PaperListResponse
// @Router /paper [get]
func (controller *paperController) List(c *gin.Context) {
q := c.DefaultQuery("q", "")
page, err := strconv.Atoi(c.DefaultQuery("page", "1"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
limit, err := strconv.Atoi(c.DefaultQuery("limit", "10"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
user_id := c.GetInt64("sub")
result, err := controller.service.List(user_id, q, page, limit)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
totalNumber, err := controller.service.Total(user_id, q)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
totalPage := int64(totalNumber/int64(limit) + 1)
response := models.PaperListResponse{
Data: *result,
Total: totalNumber,
Page: page,
TotalPage: totalPage,
PageSize: limit,
}
c.JSON(http.StatusOK, response)
}
// Get paper
//
// @Summary 페이퍼 정보 가져오기
// @Description ID로 페이퍼 정보를 가져옵니다.
// @Tags 페이퍼
//
// @Accept json
// @Produce json
//
// @Security Bearer
//
// @Param paper_id path int64 true "Paper ID"
//
// @Success 200 {object} models.PaperResponse
// @Router /paper/{paper_id} [get]
func (controller *paperController) Find(c *gin.Context) {
paper_id, err := strconv.ParseInt(c.Param("paper_id"), 10, 64)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
result, err := controller.service.Find(paper_id)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, result)
}
// Start Paper
//
// @Summary 퀴즈 시작
// @Description 퀴즈 시작합니다.
// @Tags 페이퍼
//
// @Accept json
// @Produce json
//
// @Security Bearer
//
// @Param paper_id path int64 true "Paper ID"
//
// @Success 200 {object} models.Paper
// @Router /paper/{paper_id}/start [patch]
func (controller *paperController) Start(c *gin.Context) {
paper_id, err := strconv.ParseInt(c.Param("paper_id"), 10, 64)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
paper, err := controller.service.Get(paper_id)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if paper.Status != "ready" {
c.JSON(http.StatusBadRequest, gin.H{"error": "퀴즈를 시작할 수 없습니다."})
return
}
now := time.Now()
paper.Status = "start"
paper.StartAt = &now
result, err := controller.service.Update(paper)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, result)
}
// Finish Paper
//
// @Summary 퀴즈 종료
// @Description 퀴즈 종료합니다.
// @Tags 페이퍼
//
// @Accept json
// @Produce json
//
// @Security Bearer
//
// @Param paper_id path int64 true "Paper ID"
//
// @Success 200 {object} models.Paper
// @Router /paper/{paper_id}/finish [patch]
func (controller *paperController) Finish(c *gin.Context) {
paper_id, err := strconv.ParseInt(c.Param("paper_id"), 10, 64)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
paper, err := controller.service.Get(paper_id)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if paper.Status != "start" {
c.JSON(http.StatusBadRequest, gin.H{"error": "퀴즈를 종료할 수 없습니다."})
return
}
now := time.Now()
paper.Status = "finish"
paper.DoneAt = &now
result, err := controller.service.Update(paper)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, result)
}
// Quiz List
//
// @Summary 퀴즈페이퍼 퀴즈 목록 가져오기
// @Description 퀴즈페이퍼 퀴즈 목록을 가져옵니다.
// @Tags 페이퍼
//
// @Accept json
// @Produce json
//
// @Security Bearer
//
// @Param paper_id path int64 true "퀴즈페이퍼 ID"
// @Param page query int false "페이지"
// @Param limit query int false "페이지 사이즈"
//
// @Success 200 {object} models.UserQuizListResponse
// @Router /paper/{paper_id}/quiz [get]
func (controller *paperController) QuizList(c *gin.Context) {
paper_id, err := strconv.ParseInt(c.Param("paper_id"), 10, 64)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
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.paperQuizService.List(paper_id, page, limit)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
totalNumber, err := controller.paperQuizService.Total(paper_id)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
totalPage := int64(totalNumber/int64(limit) + 1)
c.JSON(http.StatusOK, gin.H{"data": result, "total": totalNumber, "page": page, "totalPage": totalPage, "pageSize": limit})
}
// Get Quiz
//
// @Summary 퀴즈페이퍼 퀴즈 가져오기
// @Description ID로 퀴즈를 가져옵니다.
// @Tags 페이퍼
//
// @Accept json
// @Produce json
//
// @Security Bearer
//
// @Param paper_id path string true "Paper ID"
// @Param quiz_id path string true "사용자퀴즈 ID"
//
// @Success 200 {object} models.UserQuizResponse
// @Router /paper/{paper_id}/quiz/{quiz_id} [get]
func (controller *paperController) QuizFind(c *gin.Context) {
paper_id, err := strconv.ParseInt(c.Param("paper_id"), 10, 64)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
quiz_id, err := strconv.ParseInt(c.Param("quiz_id"), 10, 64)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
result, err := controller.paperQuizService.Find(quiz_id)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if paper_id != result.UserQuizPaperID {
c.JSON(http.StatusBadRequest, gin.H{"error": "퀴즈시트와 퀴즈가 일치하지 않습니다."})
return
}
c.JSON(http.StatusOK, result)
}
// Answer Quiz
//
// @Summary 퀴즈 답변
// @Description 퀴즈 답변을 제출합니다..
// @Tags 페이퍼
//
// @Accept json
// @Produce json
//
// @Security Bearer
//
// @Param paper_id path int64 true "Paper ID"
// @Param quiz_id path int64 true "Quiz ID"
// @Param quizBody body models.AnswerQuizRequest true "Answer Body"
//
// @Router /paper/{paper_id}/quiz/{quiz_id}/answer [patch]
func (controller *paperController) QuizAnswer(c *gin.Context) {
paper_id, err := strconv.ParseInt(c.Param("paper_id"), 10, 64)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
id, err := strconv.ParseInt(c.Param("quiz_id"), 10, 64)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
var request models.AnswerQuizRequest
if err := c.ShouldBindJSON(&request); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
userquiz, err := controller.userQuizService.Find(id)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if paper_id != userquiz.UserQuizPaperID {
c.JSON(http.StatusBadRequest, gin.H{"error": "퀴즈시트와 퀴즈가 일치하지 않습니다."})
return
}
user_id := c.GetInt64("sub")
if user_id != userquiz.UserID {
c.JSON(http.StatusBadRequest, gin.H{"error": "Wrong user"})
return
}
if userquiz.Status != "ready" {
c.JSON(http.StatusBadRequest, gin.H{"error": "Answered"})
return
}
quiz, err := controller.quizService.Find(userquiz.QuizID)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
var userAnswer datatypes.JSON
userAnswer, err = json.Marshal(request.Answer)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
isCorrect := cmp.Equal(quiz.Answer.String(), userAnswer.String())
var score float32 = 0.0
var correction string = "wrong"
if isCorrect {
score = 1.0
correction = "correct"
}
userquiz.Answer = userAnswer
userquiz.Status = "answer"
userquiz.Result = correction
userquiz.Score = score
_, err = controller.userQuizService.Update(userquiz)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
_, err = controller.service.UpdateScore(userquiz.UserQuizPaperID)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"message": "success"})
}

View File

@ -11,6 +11,7 @@ import (
"learnsteam/cslms-api/internal/services"
"github.com/gin-gonic/gin"
"github.com/google/go-cmp/cmp"
"github.com/google/uuid"
"gorm.io/datatypes"
)
@ -20,15 +21,20 @@ type UserQuizController interface {
Find(*gin.Context)
Create(*gin.Context)
Update(*gin.Context)
Answer(*gin.Context)
}
type userQuizController struct {
service services.UserQuizService
quizService services.QuizService
userquizpaperService services.UserQuizPaperService
}
func NewUserQuizController(service services.UserQuizService) UserQuizController {
func NewUserQuizController(service services.UserQuizService, quizService services.QuizService, userquizpaperService services.UserQuizPaperService) UserQuizController {
return &userQuizController{
service: service,
quizService: quizService,
userquizpaperService: userquizpaperService,
}
}
@ -154,7 +160,7 @@ func (controller *userQuizController) Create(c *gin.Context) {
return
}
quiz := &models.UserQuiz{
userquiz := &models.UserQuiz{
GUID: uuid.NewString(),
CenterID: request.CenterID,
UserQuizPaperID: request.UserQuizPaperID,
@ -168,7 +174,7 @@ func (controller *userQuizController) Create(c *gin.Context) {
CreatedAt: time.Now(),
}
result, err := controller.service.Create(quiz)
result, err := controller.service.Create(userquiz)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
@ -212,18 +218,6 @@ func (controller *userQuizController) Update(c *gin.Context) {
return
}
// CenterID int64 `json:"center_id" example:"1000001"`
// UserQuizPaperID int64 `json:"quiz_paper_id" example:"1000001"`
// UserID int64 `json:"user_id" example:"1000001"`
// No int `json:"vol_no" example:"1"`
// QuestionType string `json:"question_type" example:"choice"`
// Question string `json:"question" example:"질문입니다."`
// Content datatypes.JSON `json:"content"`
// Answer datatypes.JSON `json:"answer"`
// Status string `json:"status" example:"waiting"`
// Result string `json:"result" example:"success"`
// Score float32 `json:"score" example:"10"`
var content datatypes.JSON
content, err = json.Marshal(request.Content)
if err != nil {
@ -256,3 +250,91 @@ func (controller *userQuizController) Update(c *gin.Context) {
c.JSON(http.StatusOK, result)
}
// Answer Quiz
//
// @Summary 사용자퀴즈 답변
// @Description 사용자퀴즈 답변을 제출합니다..
// @Tags 사용자퀴즈
//
// @Accept json
// @Produce json
//
// @Security Bearer
//
// @Param id path int64 true "Quiz ID"
// @Param quizBody body models.AnswerQuizRequest true "Answer Body"
//
// @Router /userquiz/{id} [patch]
func (controller *userQuizController) Answer(c *gin.Context) {
id, err := strconv.ParseInt(c.Param("id"), 10, 64)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
var request models.AnswerQuizRequest
if err := c.ShouldBindJSON(&request); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
userquiz, err := controller.service.Find(id)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
user_id := c.GetInt64("sub")
if user_id != userquiz.UserID {
c.JSON(http.StatusBadRequest, gin.H{"error": "Wrong user"})
return
}
if userquiz.Status != "ready" {
c.JSON(http.StatusBadRequest, gin.H{"error": "Answered"})
return
}
quiz, err := controller.quizService.Find(userquiz.QuizID)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
var userAnswer datatypes.JSON
userAnswer, err = json.Marshal(request.Answer)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
isCorrect := cmp.Equal(quiz.Answer.String(), userAnswer.String())
var score float32 = 0.0
var correction string = "wrong"
if isCorrect {
score = 1.0
correction = "correct"
}
userquiz.Answer = userAnswer
userquiz.Status = "answer"
userquiz.Result = correction
userquiz.Score = score
_, err = controller.service.Update(userquiz)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
_, err = controller.userquizpaperService.UpdateScore(userquiz.UserQuizPaperID)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// fmt.Println("userscore : ", result.UserScore)
c.JSON(http.StatusOK, gin.H{"message": "success"})
}

59
internal/models/paper.go Normal file
View File

@ -0,0 +1,59 @@
package models
import (
"time"
)
type Paper struct {
ID int64 `json:"id" db:"id" example:"1000015" gorm:"column:id;primary_key;"`
GUID string `json:"guid_id" db:"guid_id" example:"7f9329f5-2e36-4638-92d2-73064b7291a4" gorm:"column:guid_id;size:255;uniqueIndex"`
CenterID int64 `json:"center_id" db:"center_id" example:"1000015" gorm:"column:center_id;index;"`
QuizPaperID int64 `json:"quiz_paper_id" db:"quiz_paper_id" example:"1000001" gorm:"column:quiz_paper_id;index;"`
UserID int64 `json:"user_id" db:"user_id" example:"1000002" gorm:"column:user_id;index;"`
UserScore float32 `json:"user_score" db:"user_score" example:"5" gorm:"column:user_score;"`
TotalScore float32 `json:"total_score" db:"total_score" example:"100" gorm:"column:total_score;"`
Status string `json:"status" example:"wating" gorm:"column:status;size:10;index;"`
StartAt *time.Time `json:"start_at" gorm:"column:start_at;index;"`
DoneAt *time.Time `json:"done_at" gorm:"column:done_at;index;"`
UpdatedAt time.Time `json:"-" gorm:"column:updated_at;type:DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;index;->:false"`
CreatedAt time.Time `json:"-" gorm:"column:created_at;type:DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP;index;->:false;<-:create"`
}
// 테이블이름 정의
func (Paper) TableName() string {
return "UserQuizPapers"
}
type PaperResponse struct {
ID int64 `json:"id" db:"id" example:"1000015"`
GUID string `json:"guid_id" db:"guid_id" example:"7f9329f5-2e36-4638-92d2-73064b7291a4"`
CenterID int64 `json:"center_id" db:"center_id" example:"1000015"`
QuizPaperID int64 `json:"quiz_paper_id" db:"quiz_paper_id" example:"1000001"`
UserID int64 `json:"user_id" db:"user_id" example:"1000002"`
FirstName string `json:"first_name" db:"first_name" example:"길순"`
LastName string `json:"last_name" db:"last_name" example:"홍"`
UserScore float32 `json:"user_score" db:"user_score" example:"5"`
TotalScore float32 `json:"total_score" db:"total_score" example:"100"`
Status string `json:"status" example:"wating"`
Category string `json:"category" example:"파이썬기본"`
Title string `json:"title" example:"출력 Print"`
StartAt *time.Time `json:"start_at"`
DoneAt *time.Time `json:"done_at"`
CreatedAt *time.Time `json:"created_at"`
}
type PaperPatchRequest struct {
UserScore float32 `json:"user_score" example:"4"`
TotalScore float32 `json:"total_score" example:"80"`
Status string `json:"status" example:"ready"`
StartAt string `json:"start_at,omitempty" example:"2023-11-10T13:10:00+09:00"`
DoneAt string `json:"done_at,omitempty" example:"2023-11-10T13:25:00+09:00"`
}
type PaperListResponse struct {
Data []PaperResponse `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"`
}

View File

@ -0,0 +1,54 @@
package models
import (
"time"
"gorm.io/datatypes"
)
type PaperQuiz struct {
ID int64 `json:"id" db:"id" example:"1000001" gorm:"column:id;primary_key;"`
GUID string `json:"guid_id" db:"guid_id" example:"2036023a-fb56-4b6c-b3bb-c787c681ada6" gorm:"column:guid_id;size:255;index;"`
CenterID int64 `json:"center_id" db:"center_id" example:"100001" gorm:"column:center_id;index;"`
UserQuizPaperID int64 `json:"user_quiz_paper_id" db:"user_quiz_paper_id" example:"1000001" gorm:"column:user_quiz_paper_id;index;"`
UserID int64 `json:"user_id" db:"user_id" example:"1000001" gorm:"column:user_id;index;"`
QuizID int64 `json:"quiz_id" db:"quiz_id" example:"1000001" gorm:"column:quiz_id;index;"`
No int `json:"vol_no" db:"vol_no" example:"5" gorm:"column:vol_no;index;"`
QuestionType string `json:"question_type" db:"question_type" example:"choice" gorm:"column:question_type;size:10;index;"`
Question string `json:"question" db:"question" example:"퀴즈 질문입니다." gorm:"column:question;size:512;"`
Content datatypes.JSON `json:"content" db:"content" gorm:"column:content;"`
Status string `json:"status" db:"status" example:"waiting" gorm:"column:status;size:10;index;"`
Result string `json:"result" db:"result" example:"success" gorm:"column:result;size:10;index;"`
Score float32 `json:"score" db:"score" example:"10" gorm:"column:score;"`
UpdatedAt time.Time `json:"-" gorm:"column:updated_at;type:DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;index;->:false"`
CreatedAt time.Time `json:"-" gorm:"column:created_at;type:DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP;index;->:false;<-:create"`
}
// 테이블이름 정의
func (PaperQuiz) TableName() string {
return "UserQuizs"
}
type PaperQuizResponse struct {
ID int64 `json:"id" example:"1000001"`
GUID string `json:"guid_id" example:"2036023a-fb56-4b6c-b3bb-c787c681ada6"`
CenterD string `json:"center_id" example:"2036023a-fb56-4b6c-b3bb-c787c681ada6"`
UserQuizPaperID int64 `json:"user_quiz_paper_id" example:"1000001"`
UserID int64 `json:"user_id" example:"1000001"`
QuizID int64 `json:"quiz_id" example:"1000001"`
No int `json:"vol_no" example:"5"`
QuestionType string `json:"question_type" example:"check"`
Question string `json:"question" example:"퀴즈 질문입니다."`
Content QuizContent `json:"content"`
Status string `json:"status" example:"waiting"`
Result string `json:"result" example:"success"`
Score float32 `json:"score" example:"10"`
}
type PaperQuizListResponse struct {
Data []UserQuizResponse `json:"data"`
Total int64 `json:"total" example:"5"`
Page int `json:"page" example:"1"`
TotalPage int64 `json:"totalPage" example:"1"`
PageSize int `json:"pageSize" example:"10"`
}

View File

@ -69,3 +69,7 @@ type UserQuizListResponse struct {
TotalPage int64 `json:"totalPage" example:"1"`
PageSize int `json:"pageSize" example:"10"`
}
type AnswerQuizRequest struct {
Answer []string `json:"answer" example:"option1,option4"`
}

View File

@ -0,0 +1,101 @@
package repositories
import (
"learnsteam/cslms-api/internal/models"
"gorm.io/gorm"
)
type paperRepository struct {
DB *gorm.DB
}
func NewPaperRepository(db *gorm.DB) PaperRepository {
return &paperRepository{
DB: db,
}
}
type PaperRepository interface {
List(int64, string, int, int) (*[]models.PaperResponse, error)
Total(int64, string) (int64, error)
Find(int64) (*models.PaperResponse, error)
Get(int64) (*models.Paper, error)
Update(*models.Paper) (*models.Paper, error)
}
func (r *paperRepository) List(user_id int64, q string, page int, limit int) (*[]models.PaperResponse, error) {
var rows *[]models.PaperResponse
var err error
offset := limit * (page - 1)
if q != "" {
err = r.DB.Offset(offset).Limit(limit).Order("UserQuizPapers.created_at DESC").
Table("UserQuizPapers").
Select("UserQuizPapers.*", "Users.first_name", "Users.last_name", "QuizPapers.title", "QuizPapers.category").
Joins("LEFT JOIN Users ON Users.id = UserQuizPapers.user_id").
Joins("LEFT JOIN QuizPapers ON QuizPapers.id = UserQuizPapers.quiz_paper_id").
Where("QuizPapers.title LIKE ? OR QuizPapers.category LIKE ? OR CONCAT(first_name, last_name) LIKE ?", "%"+q+"%", "%"+q+"%", "%"+q+"%").
Where("UserQuizPapers.user_id = ?", user_id).
Find(&rows).Error
} else {
err = r.DB.Offset(offset).Limit(limit).Order("UserQuizPapers.created_at DESC").
Table("UserQuizPapers").
Select("UserQuizPapers.*", "Users.first_name", "Users.last_name", "QuizPapers.title", "QuizPapers.category").
Joins("LEFT JOIN Users ON Users.id = UserQuizPapers.user_id").
Joins("LEFT JOIN QuizPapers ON QuizPapers.id = UserQuizPapers.quiz_paper_id").
Where("UserQuizPapers.user_id = ?", user_id).
Find(&rows).Error
}
return rows, err
}
func (r *paperRepository) Total(user_id int64, q string) (int64, error) {
var total int64
var err error
if q != "" {
err = r.DB.Model(&models.Paper{}).
Joins("LEFT JOIN Users ON Users.id = UserQuizPapers.user_id").
Joins("LEFT JOIN QuizPapers ON QuizPapers.id = UserQuizPapers.quiz_paper_id").
Where("QuizPapers.title LIKE ? OR QuizPapers.category LIKE ? OR CONCAT(first_name, last_name) LIKE ?", "%"+q+"%", "%"+q+"%", "%"+q+"%").
Where("UserQuizPapers.user_id = ?", user_id).
Count(&total).
Error
} else {
err = r.DB.Model(&models.Paper{}).
Where("user_id = ?", user_id).
Count(&total).
Error
}
return total, err
}
func (r *paperRepository) Find(id int64) (*models.PaperResponse, error) {
var row *models.PaperResponse
err := r.DB.
Table("UserQuizPapers").
Select("UserQuizPapers.*", "Users.first_name", "Users.last_name", "QuizPapers.title", "QuizPapers.category").
Joins("LEFT JOIN Users ON Users.id = UserQuizPapers.user_id").
Joins("LEFT JOIN QuizPapers ON QuizPapers.id = UserQuizPapers.quiz_paper_id").
Where("UserQuizPapers.id = ?", id).First(&row).Error
return row, err
}
func (r *paperRepository) Get(id int64) (*models.Paper, error) {
var row *models.Paper
if err := r.DB.Where("id=?", id).First(&row).Error; err != nil {
return nil, err
}
return row, nil
}
func (r *paperRepository) Update(paper *models.Paper) (*models.Paper, error) {
var row *models.Paper
if err := r.DB.Where("id=?", paper.ID).First(&row).Error; err != nil {
return nil, err
}
err := r.DB.Model(&row).Select("*").Updates(&paper).Error
return row, err
}

View File

@ -0,0 +1,89 @@
package repositories
import (
"learnsteam/cslms-api/internal/models"
"gorm.io/gorm"
)
type paperQuizRepository struct {
DB *gorm.DB
}
func NewPaperQuizRepository(db *gorm.DB) PaperQuizRepository {
return &paperQuizRepository{
DB: db,
}
}
type PaperQuizRepository interface {
List(int64, int, int) (*[]models.PaperQuiz, error)
Total(int64) (int64, error)
Find(int64) (*models.PaperQuiz, error)
Update(*models.PaperQuiz) (*models.PaperQuiz, error)
Delete(int64) error
SumScore(int64) (float32, error)
}
func (r *paperQuizRepository) List(paper_id int64, page int, limit int) (*[]models.PaperQuiz, error) {
var rows *[]models.PaperQuiz
var err error
offset := limit * (page - 1)
if paper_id > 0 {
err = r.DB.Offset(offset).Limit(limit).Order("vol_no ASC").Where("user_quiz_paper_id = ?", paper_id).Find(&rows).Error
} else {
err = r.DB.Offset(offset).Limit(limit).Find(&rows).Error
}
return rows, err
}
func (r *paperQuizRepository) Total(paper_id int64) (int64, error) {
var total int64
var err error
if paper_id > 0 {
err = r.DB.Model(&models.PaperQuiz{}).Where("user_quiz_paper_id = ?", paper_id).Count(&total).Error
} else {
err = r.DB.Model(&models.PaperQuiz{}).Count(&total).Error
}
return total, err
}
func (r *paperQuizRepository) Find(id int64) (*models.PaperQuiz, error) {
var row *models.PaperQuiz
err := r.DB.Where("id = ?", id).First(&row).Error
return row, err
}
func (r *paperQuizRepository) Update(quiz *models.PaperQuiz) (*models.PaperQuiz, error) {
var row *models.PaperQuiz
if err := r.DB.Where("id=?", quiz.ID).First(&row).Error; err != nil {
return nil, err
}
err := r.DB.Model(&row).Select("*").Updates(&quiz).Error
return row, err
}
func (r *paperQuizRepository) Delete(id int64) error {
var row *models.PaperQuiz
if err := r.DB.Where("id=?", id).First(&row).Error; err != nil {
return err
}
err := r.DB.Delete(&row).Error
return err
}
func (r *paperQuizRepository) SumScore(paper_id int64) (float32, error) {
var result struct {
Score float32
}
if err := r.DB.Model(&models.PaperQuiz{}).Select("SUM(score) AS score").
Where("user_quiz_paper_id = ?", paper_id).
Scan(&result).Error; err != nil {
return 0, err
}
return result.Score, nil
}

View File

@ -26,6 +26,7 @@ type UserQuizRepository interface {
Insert([]models.UserQuiz) ([]models.UserQuiz, error)
Update(*models.UserQuiz) (*models.UserQuiz, error)
Delete(int64) error
SumScore(int64) (float32, error)
}
func (r *userQuizRepository) List(user_quiz_paper_id int64, page int, limit int) (*[]models.UserQuiz, error) {
@ -86,3 +87,16 @@ func (r *userQuizRepository) Delete(id int64) error {
err := r.DB.Delete(&row).Error
return err
}
func (r *userQuizRepository) SumScore(userquizpaper_id int64) (float32, error) {
var result struct {
Score float32
}
if err := r.DB.Model(&models.UserQuiz{}).Select("SUM(score) AS score").
Where("user_quiz_paper_id = ?", userquizpaper_id).
Scan(&result).Error; err != nil {
return 0, err
}
return result.Score, nil
}

89
internal/routers/paper.go Normal file
View File

@ -0,0 +1,89 @@
package routers
import (
"learnsteam/cslms-api/internal/controllers"
"learnsteam/cslms-api/internal/middleware"
"learnsteam/cslms-api/internal/repositories"
"learnsteam/cslms-api/internal/services"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
func InitPaperRouter(db *gorm.DB, router *gin.Engine) {
r := NewPaperRouter(db, router)
r.SetPaperRouter()
}
type PaperRouter interface {
SetRouter(db *gorm.DB, router *gin.Engine)
}
type paperRouter struct {
db *gorm.DB
repository repositories.PaperRepository
paperQuizRepository repositories.PaperQuizRepository
quizRepository repositories.QuizRepository
quizPaperRepository repositories.QuizPaperRepository
userRepository repositories.UserRepository
userQuizRepository repositories.UserQuizRepository
service services.PaperService
paperQuizService services.PaperQuizService
quizService services.QuizService
quizPaperService services.QuizPaperService
userService services.UserService
userQuizService services.UserQuizService
controller controllers.PaperController
router *gin.Engine
}
func NewPaperRouter(db *gorm.DB, router *gin.Engine) *paperRouter {
repository := repositories.NewPaperRepository(db)
paperQuizRepository := repositories.NewPaperQuizRepository(db)
quizRepository := repositories.NewQuizRepository(db)
quizPaperRepository := repositories.NewQuizPaperRepository(db)
tokenRepository := repositories.NewTokenRepository(db)
userRepository := repositories.NewUserRepository(db)
userQuizRepository := repositories.NewUserQuizRepository(db)
service := services.NewPaperService(repository, paperQuizRepository)
paperQuizService := services.NewPaperQuizService(paperQuizRepository)
quizService := services.NewQuizService(quizRepository)
quizPaperService := services.NewQuizPaperService(quizPaperRepository)
userService := services.NewUserService(userRepository, tokenRepository)
userQuizService := services.NewUserQuizService(userQuizRepository, quizRepository)
controller := controllers.NewPaperController(service, paperQuizService, quizService, quizPaperService, userService, userQuizService)
return &paperRouter{
db: db,
repository: repository,
quizRepository: quizRepository,
quizPaperRepository: quizPaperRepository,
userRepository: userRepository,
userQuizRepository: userQuizRepository,
service: service,
paperQuizService: paperQuizService,
quizService: quizService,
quizPaperService: quizPaperService,
userService: userService,
userQuizService: userQuizService,
controller: controller,
router: router,
}
}
func (r *paperRouter) SetPaperRouter() {
group := r.router.Group("/paper")
group.GET("", middleware.Auth("member"), r.controller.List)
group.GET("/:paper_id", middleware.Auth("member"), r.controller.Find)
group.PATCH("/:paper_id/start", middleware.Auth("member"), r.controller.Start)
group.PATCH("/:paper_id/finish", middleware.Auth("member"), r.controller.Finish)
group.GET("/:paper_id/quiz", middleware.Auth("member"), r.controller.QuizList)
group.GET("/:paper_id/quiz/:quiz_id", middleware.Auth("member"), r.controller.QuizFind)
group.PATCH("/:paper_id/quiz/:quiz_id/answer", middleware.Auth("member"), r.controller.QuizAnswer)
}

View File

@ -25,6 +25,7 @@ func Init() {
InitAuthRouter(maindb, Router)
InitCenterRouter(maindb, Router)
InitPaperRouter(maindb, Router)
InitQuizPaperRouter(maindb, Router)
InitQuizRouter(maindb, Router)
InitTokenRouter(maindb, Router)

View File

@ -23,7 +23,12 @@ type userQuizRouter struct {
db *gorm.DB
repository repositories.UserQuizRepository
quizRepository repositories.QuizRepository
userquizRepository repositories.UserQuizRepository
userquizpaperRepository repositories.UserQuizPaperRepository
service services.UserQuizService
quizService services.QuizService
userquizService services.UserQuizService
userquizpaperService services.UserQuizPaperService
controller controllers.UserQuizController
router *gin.Engine
}
@ -31,14 +36,24 @@ type userQuizRouter struct {
func NewUserQuizRouter(db *gorm.DB, router *gin.Engine) *userQuizRouter {
repository := repositories.NewUserQuizRepository(db)
quizRepostory := repositories.NewQuizRepository(db)
userquizRepository := repositories.NewUserQuizRepository(db)
userquizpaperRepository := repositories.NewUserQuizPaperRepository(db)
service := services.NewUserQuizService(repository, quizRepostory)
controller := controllers.NewUserQuizController(service)
quizService := services.NewQuizService(quizRepostory)
userquizService := services.NewUserQuizService(userquizRepository, quizRepostory)
userquizpaperService := services.NewUserQuizPaperService(userquizpaperRepository, userquizRepository)
controller := controllers.NewUserQuizController(service, quizService, userquizpaperService)
return &userQuizRouter{
db: db,
repository: repository,
quizRepository: quizRepostory,
userquizpaperRepository: userquizpaperRepository,
userquizRepository: userquizRepository,
service: service,
quizService: quizService,
userquizpaperService: userquizpaperService,
userquizService: userquizService,
controller: controller,
router: router,
}
@ -50,4 +65,5 @@ func (r *userQuizRouter) SetUserQuizRouter() {
group.GET("/:id", middleware.Auth("admin"), r.controller.Find)
group.POST("", middleware.Auth("admin"), r.controller.Create)
group.PUT("/:id", middleware.Auth("admin"), r.controller.Update)
group.PATCH("/:id", middleware.Auth("member"), r.controller.Answer)
}

View File

@ -42,7 +42,7 @@ func NewUserQuizPaperRouter(db *gorm.DB, router *gin.Engine) *userQuizPaperRoute
userRepository := repositories.NewUserRepository(db)
userQuizRepository := repositories.NewUserQuizRepository(db)
service := services.NewUserQuizPaperService(repository)
service := services.NewUserQuizPaperService(repository, userQuizRepository)
quizService := services.NewQuizService(quizRepository)
quizPaperService := services.NewQuizPaperService(quizPaperRepository)
userService := services.NewUserService(userRepository, tokenRepository)

View File

@ -0,0 +1,66 @@
package services
import (
"learnsteam/cslms-api/internal/models"
"learnsteam/cslms-api/internal/repositories"
)
type paperService struct {
repository repositories.PaperRepository
paperQuizRepository repositories.PaperQuizRepository
}
type PaperService interface {
List(int64, string, int, int) (*[]models.PaperResponse, error)
Total(int64, string) (int64, error)
Find(int64) (*models.PaperResponse, error)
Get(int64) (*models.Paper, error)
Update(*models.Paper) (*models.Paper, error)
UpdateScore(int64) (*models.Paper, error)
}
func NewPaperService(repository repositories.PaperRepository, paperQuizRepository repositories.PaperQuizRepository) PaperService {
return &paperService{
repository: repository,
paperQuizRepository: paperQuizRepository,
}
}
func (s *paperService) List(user_id int64, q string, page int, limit int) (*[]models.PaperResponse, error) {
return s.repository.List(user_id, q, page, limit)
}
func (s *paperService) Total(user_id int64, q string) (int64, error) {
return s.repository.Total(user_id, q)
}
func (s *paperService) Find(id int64) (*models.PaperResponse, error) {
return s.repository.Find(id)
}
func (s *paperService) Get(id int64) (*models.Paper, error) {
return s.repository.Get(id)
}
func (s *paperService) Update(paper *models.Paper) (*models.Paper, error) {
result, err := s.repository.Update(paper)
return result, err
}
func (s *paperService) UpdateScore(userquizpaper_id int64) (*models.Paper, error) {
score, err := s.paperQuizRepository.SumScore(userquizpaper_id)
if err != nil {
return nil, err
}
userquizpaper, err := s.repository.Get(userquizpaper_id)
if err != nil {
return nil, err
}
userquizpaper.UserScore = score
return s.repository.Update(userquizpaper)
}

View File

@ -0,0 +1,43 @@
package services
import (
"learnsteam/cslms-api/internal/models"
"learnsteam/cslms-api/internal/repositories"
)
type paperQuizService struct {
repository repositories.PaperQuizRepository
quizRepository repositories.QuizRepository
}
type PaperQuizService interface {
List(int64, int, int) (*[]models.PaperQuiz, error)
Total(int64) (int64, error)
Find(int64) (*models.PaperQuiz, error)
Update(*models.PaperQuiz) (*models.PaperQuiz, error)
}
func NewPaperQuizService(repository repositories.PaperQuizRepository) PaperQuizService {
return &paperQuizService{
repository: repository,
}
}
func (s *paperQuizService) List(user_quiz_paper_id int64, page int, limit int) (*[]models.PaperQuiz, error) {
return s.repository.List(user_quiz_paper_id, page, limit)
}
func (s *paperQuizService) Total(user_quiz_paper_id int64) (int64, error) {
return s.repository.Total(user_quiz_paper_id)
}
func (s *paperQuizService) Find(id int64) (*models.PaperQuiz, error) {
return s.repository.Find(id)
}
func (s *paperQuizService) Update(quiz *models.PaperQuiz) (*models.PaperQuiz, error) {
result, err := s.repository.Update(quiz)
return result, err
}

View File

@ -84,6 +84,7 @@ func (s *userQuizService) Generate(quiz_paper_id int64, userQuizPaper *models.Us
QuestionType: quiz.QuestionType,
Question: quiz.Question,
Content: quiz.Content,
Status: "ready",
}
fmt.Println(userQuiz.Content)

View File

@ -1,6 +1,7 @@
package services
import (
"fmt"
"learnsteam/cslms-api/internal/models"
"learnsteam/cslms-api/internal/repositories"
@ -9,6 +10,7 @@ import (
type userQuizPaperService struct {
repository repositories.UserQuizPaperRepository
userquizRepository repositories.UserQuizRepository
}
type UserQuizPaperService interface {
@ -22,11 +24,13 @@ type UserQuizPaperService interface {
Update(*models.UserQuizPaper) (*models.UserQuizPaper, error)
Delete(string) error
Generate([]int64, *models.QuizPaper) []models.UserQuizPaper
UpdateScore(int64) (*models.UserQuizPaper, error)
}
func NewUserQuizPaperService(repository repositories.UserQuizPaperRepository) UserQuizPaperService {
func NewUserQuizPaperService(repository repositories.UserQuizPaperRepository, userquizRepository repositories.UserQuizRepository) UserQuizPaperService {
return &userQuizPaperService{
repository: repository,
userquizRepository: userquizRepository,
}
}
@ -79,6 +83,7 @@ func (s *userQuizPaperService) Generate(users []int64, quizPaper *models.QuizPap
CenterID: quizPaper.CenterID,
UserID: user_id,
QuizPaperID: quizPaper.ID,
Status: "ready",
}
userQuizPapers = append(userQuizPapers, userQuizPaper)
@ -86,3 +91,19 @@ func (s *userQuizPaperService) Generate(users []int64, quizPaper *models.QuizPap
return userQuizPapers
}
func (s *userQuizPaperService) UpdateScore(userquizpaper_id int64) (*models.UserQuizPaper, error) {
score, err := s.userquizRepository.SumScore(userquizpaper_id)
if err != nil {
return nil, err
}
userquizpaper, err := s.repository.Get(userquizpaper_id)
if err != nil {
return nil, err
}
fmt.Println("score", score)
userquizpaper.UserScore = score
return s.repository.Update(userquizpaper)
}

0
test/test.db Normal file
View File

View File

@ -1,6 +1,7 @@
package learsteam_quiz_test
import (
"encoding/json"
"fmt"
config "learnsteam/cslms-api/configs"
"learnsteam/cslms-api/internal/controllers"
@ -10,8 +11,10 @@ import (
"time"
"github.com/golang-jwt/jwt/v5"
"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/suite"
"github.com/tj/assert"
"gorm.io/datatypes"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/schema"
@ -295,3 +298,24 @@ func (suite *TokenTestSuite) TestVerifyExpiredToken() {
assert.Error(suite.T(), err)
assert.False(suite.T(), jwtToken.Valid)
}
func (suite *TokenTestSuite) TestCompareJSON() {
// a := `["a","b","c"]`
// b := `["a","b","c"]`
a := `{"x":1,"y":2}`
b := `{"y":2,"x":1}`
var json1 datatypes.JSON
json1, err := json.Marshal(a)
assert.NoError(suite.T(), err)
var json2 datatypes.JSON
json2, err = json.Marshal(b)
assert.NoError(suite.T(), err)
assert.Equal(suite.T(), json2.String(), "")
compare := cmp.Equal(json1.String(), json2.String())
assert.Equal(suite.T(), true, compare)
}