feat: 관리자 설문 생성 추가
parent
f0f09432c3
commit
83a0fbdad9
|
|
@ -0,0 +1,85 @@
|
|||
/target
|
||||
.DS_Store
|
||||
._.DS_Store
|
||||
**/.DS_Store
|
||||
**/._.DS_Store
|
||||
|
||||
|
||||
HELP.md
|
||||
.gradle
|
||||
build/
|
||||
!gradle/wrapper/gradle-wrapper.jar
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
bin/
|
||||
!**/src/main/**/bin/
|
||||
!**/src/test/**/bin/
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
out/
|
||||
!**/src/main/**/out/
|
||||
!**/src/test/**/out/
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
|
||||
|
||||
# Scala IDE specific (Scala & Java development for Eclipse)
|
||||
.cache-main
|
||||
.scala_dependencies
|
||||
.worksheet
|
||||
|
||||
# Uncomment this line if you wish to ignore the project description file.
|
||||
# Typically, this file would be tracked if it contains build/dependency configurations:
|
||||
#.project
|
||||
|
||||
.svn
|
||||
|
||||
# ---> Java
|
||||
# Compiled class file
|
||||
#*.class
|
||||
|
||||
# Log file
|
||||
*.log
|
||||
|
||||
# BlueJ files
|
||||
*.ctxt
|
||||
|
||||
# Mobile Tools for Java (J2ME)
|
||||
.mtj.tmp/
|
||||
|
||||
# Package Files #
|
||||
*.war
|
||||
*.nar
|
||||
*.ear
|
||||
*.zip
|
||||
*.tar.gz
|
||||
*.rar
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
replay_pid*
|
||||
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
.vs/
|
||||
|
||||
|
||||
# Added by thkim
|
||||
24
list.txt
24
list.txt
|
|
@ -1,17 +1,7 @@
|
|||
src\main\webapp\WEB-INF\jsp\sgis\com\common\header.jsp
|
||||
src\main\webapp\WEB-INF\jsp\tiles\attribute\portal.top.jsp
|
||||
src\main\webapp\WEB-INF\jsp\sgis\com\common\head.jsp
|
||||
src\main\webapp\WEB-INF\jsp\sgis\com\board\boardList.jsp
|
||||
src\main\resources\egovframework\mapper\sgis\board\PostMapper.xml
|
||||
src\main\webapp\WEB-INF\jsp\sgis\com\board\boardView.jsp
|
||||
src\main\webapp\WEB-INF\jsp\sgis\com\board\boardWrite.jsp
|
||||
src\main\resources\egovframework\mapper\sgis\board\MemberMapper.xml
|
||||
src\main\webapp\com\js\main.map.js
|
||||
src\main\webapp\WEB-INF\jsp\sgis\map\mapMain.jsp
|
||||
src\main\webapp\WEB-INF\jsp\tiles\attribute\app.submenu.jsp
|
||||
src\main\webapp\WEB-INF\jsp\sgis\map\mapInformation\sichudanNew.jsp
|
||||
src\main\webapp\com\css\cross-section.css
|
||||
src\main\webapp\com\js\cross-section.js
|
||||
src\main\resources\egovframework\mapper\sgis\map\MapMainMapper.xml
|
||||
src\main\webapp\WEB-INF\jsp\sgis\map\mapInformation\boreholeLog.jsp
|
||||
src\main\webapp\WEB-INF\jsp\sgis\surveysystem\createSurvey.jsp
|
||||
src\main\webapp\WEB-INF\jsp\sgis\surveysystem\createSurvey.jsp
|
||||
src\main\webapp\WEB-INF\jsp\tiles\attribute\adm.top.jsp
|
||||
src\main\webapp\WEB-INF\jsp\sgis\surveysystem\createQuestion.jsp
|
||||
src\main\resources\egovframework\mapper\sgis\surveysystem\SurveyMapper.xml
|
||||
src\main\resources\egovframework\mapper\sgis\surveysystem\QuestionMapper.xml
|
||||
src\main\webapp\WEB-INF\jsp\sgis\surveysystem\createQuestionOption.jsp
|
||||
src\main\webapp\WEB-INF\jsp\sgis\map\mapMain.jsp
|
||||
68
pom.xml
68
pom.xml
|
|
@ -25,36 +25,26 @@
|
|||
</properties>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>mvn2</id>
|
||||
<url>http://repo1.maven.org/maven2/</url>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>egovframe</id>
|
||||
<url>http://www.egovframe.go.kr/maven/</url>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>egovframe2</id>
|
||||
<url>http://maven.egovframe.kr:8080/maven/</url>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>mvn2s</id>
|
||||
<url>https://repo1.maven.org/maven2/</url>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>egovframe</id>
|
||||
<url>https://maven.egovframe.go.kr/maven/</url>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>osgeo</id>
|
||||
<name>OSGeo Release Repository</name>
|
||||
|
|
@ -299,22 +289,22 @@
|
|||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
<version>2.12.5</version>
|
||||
<version>2.12.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>2.12.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||
<version>2.12.5</version>
|
||||
<version>2.12.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
<version>2.12.5</version>
|
||||
<version>2.12.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||
<version>2.12.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.googlecode.json-simple</groupId>
|
||||
|
|
@ -480,7 +470,7 @@
|
|||
<build>
|
||||
<defaultGoal>install</defaultGoal>
|
||||
<directory>${basedir}/target</directory>
|
||||
<finalName>sht_webapp</finalName>
|
||||
<finalName>smart_ground</finalName>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
package sgis.com.util;
|
||||
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.AlgorithmParameters;
|
||||
import java.security.MessageDigest;
|
||||
|
|
@ -15,7 +13,6 @@ import javax.crypto.spec.PBEKeySpec;
|
|||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* @FileName : CryptoUtil.java
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import java.text.DecimalFormat;
|
|||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* @FileName : StringUtil.java
|
||||
|
|
|
|||
|
|
@ -3,10 +3,14 @@ package sgis.surveysystem.controller;
|
|||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.ui.Model; // Model 객체 사용을 위해 추가
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 관리자 설문 페이지를 위한 컨트롤러.
|
||||
* JSP 뷰 파일을 반환합니다.
|
||||
* 모든 요청은 .do 확장자로 끝납니다.
|
||||
*/
|
||||
@Controller // 뷰 이름을 반환하는 컨트롤러임을 명시
|
||||
@RequestMapping("/admin/survey") // 이 컨트롤러의 기본 URL 경로
|
||||
|
|
@ -14,15 +18,50 @@ public class AdminSurveyPageController {
|
|||
|
||||
/**
|
||||
* 설문 생성 폼 페이지를 표시합니다.
|
||||
* 웹 브라우저에서 http://localhost:8080/your-app-context/admin/survey/create 로 접근 시 이 메서드가 호출됩니다.
|
||||
* (your-app-context는 웹 애플리케이션의 컨텍스트 경로입니다. 예: /sgis)
|
||||
* 웹 브라우저에서 `/admin/survey/createSurveyForm.do` 로 접근 시 이 메서드가 호출됩니다.
|
||||
*
|
||||
* @return JSP 파일의 논리적 경로 (ViewResolver에 의해 실제 경로로 변환됨)
|
||||
*/
|
||||
@GetMapping("/create.do")
|
||||
@GetMapping("/createSurveyForm.do") // GET 요청 처리, .do 확장자 추가
|
||||
public String showCreateSurveyForm() {
|
||||
// ViewResolver 설정에 따라 다음 경로의 JSP 파일을 찾게 됩니다:
|
||||
// /WEB-INF/jsp/ + sgis/surveysystem/createSurvey + .jsp
|
||||
// /WEB-INF/jsp/sgis/surveysystem/createSurvey.jsp
|
||||
return "sgis/surveysystem/createSurvey";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 특정 설문에 대한 문항 생성 폼 페이지를 표시합니다.
|
||||
* 웹 브라우저에서 `/admin/survey/{surveyId}/createQuestionForm.do` 로 접근 시 이 메서드가 호출됩니다.
|
||||
* 이 페이지에서는 특정 설문에 속하는 문항을 생성할 수 있습니다.
|
||||
*
|
||||
* @param surveyId 문항을 생성할 설문의 고유 ID
|
||||
* @param model Spring Model 객체 (뷰로 데이터를 전달하기 위해 사용)
|
||||
* @return JSP 파일의 논리적 경로 (ViewResolver에 의해 실제 경로로 변환됨)
|
||||
*/
|
||||
@GetMapping("/{surveyId}/createQuestionForm.do") // GET 요청 처리, .do 확장자 추가
|
||||
public String showCreateQuestionForm(@PathVariable UUID surveyId, Model model) {
|
||||
// surveyId를 Model에 추가하여 JSP에서 ${surveyId}로 접근할 수 있도록 합니다.
|
||||
model.addAttribute("surveyId", surveyId);
|
||||
// ViewResolver 설정에 따라 다음 경로의 JSP 파일을 찾게 됩니다:
|
||||
// /WEB-INF/jsp/sgis/surveysystem/createQuestion.jsp
|
||||
return "sgis/surveysystem/createQuestion";
|
||||
}
|
||||
|
||||
/**
|
||||
* 특정 문항에 대한 선택지 생성 폼 페이지를 표시합니다.
|
||||
* 웹 브라우저에서 `/admin/survey/question/{questionId}/createOptionForm.do` 로 접근 시 이 메서드가 호출됩니다.
|
||||
* 이 페이지에서는 특정 문항에 속하는 선택지를 생성할 수 있습니다.
|
||||
*
|
||||
* @param questionId 선택지를 생성할 문항의 고유 ID
|
||||
* @param model Spring Model 객체 (뷰로 데이터를 전달하기 위해 사용)
|
||||
* @return JSP 파일의 논리적 경로 (ViewResolver에 의해 실제 경로로 변환됨)
|
||||
*/
|
||||
@GetMapping("/question/{questionId}/createOptionForm.do") // GET 요청 처리, .do 확장자 추가
|
||||
public String showCreateQuestionOptionForm(@PathVariable UUID questionId, Model model) {
|
||||
// questionId를 Model에 추가하여 JSP에서 ${questionId}로 접근할 수 있도록 합니다.
|
||||
model.addAttribute("questionId", questionId);
|
||||
// ViewResolver 설정에 따라 다음 경로의 JSP 파일을 찾게 됩니다:
|
||||
// /WEB-INF/jsp/sgis/surveysystem/createQuestionOption.jsp
|
||||
return "sgis/surveysystem/createQuestionOption";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import java.util.stream.Collectors;
|
|||
/**
|
||||
* 설문 문항(Question) 관련 HTTP 요청을 처리하는 REST 컨트롤러.
|
||||
* 문항 생성, 조회, 수정, 삭제 등의 API 엔드포인트를 제공합니다.
|
||||
* 모든 요청은 .do 확장자로 끝나며, GET과 POST 메서드만 사용합니다.
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/questions")
|
||||
|
|
@ -34,7 +35,7 @@ public class QuestionController {
|
|||
* @param request Question 생성 요청 DTO
|
||||
* @return 생성된 문항 정보를 담은 응답 (HTTP 201 Created)
|
||||
*/
|
||||
@PostMapping
|
||||
@PostMapping("/createQuestion.do") // POST 요청 처리, .do 확장자 추가
|
||||
public ResponseEntity<QuestionResponse> createQuestion(@RequestBody QuestionCreateRequest request) {
|
||||
QuestionResponse createdQuestion = QuestionResponse.from(
|
||||
questionService.createQuestion(
|
||||
|
|
@ -55,7 +56,7 @@ public class QuestionController {
|
|||
* @param surveyId 문항 목록을 조회할 설문 ID
|
||||
* @return 해당 설문의 모든 문항 정보를 담은 응답 리스트 (HTTP 200 OK)
|
||||
*/
|
||||
@GetMapping("/by-survey/{surveyId}")
|
||||
@GetMapping("/by-survey/{surveyId}/getQuestions.do") // GET 요청 처리, .do 확장자 추가
|
||||
public ResponseEntity<List<QuestionResponse>> getQuestionsBySurveyId(@PathVariable UUID surveyId) {
|
||||
List<QuestionResponse> questions = questionService.getQuestionsBySurveyId(surveyId).stream()
|
||||
.map(question -> {
|
||||
|
|
@ -81,7 +82,7 @@ public class QuestionController {
|
|||
* @return 조회된 문항 정보를 담은 응답 (HTTP 200 OK)
|
||||
* 문항이 없을 경우 HTTP 404 Not Found 반환
|
||||
*/
|
||||
@GetMapping("/{questionId}")
|
||||
@GetMapping("/{questionId}/getQuestion.do") // GET 요청 처리, .do 확장자 추가
|
||||
public ResponseEntity<QuestionResponse> getQuestionById(@PathVariable UUID questionId) {
|
||||
try {
|
||||
QuestionResponse question = QuestionResponse.from(questionService.getQuestionById(questionId));
|
||||
|
|
@ -99,14 +100,15 @@ public class QuestionController {
|
|||
}
|
||||
|
||||
/**
|
||||
* 문항 정보를 업데이트합니다. (HTTP PUT)
|
||||
* 문항 정보를 업데이트합니다. (HTTP POST)
|
||||
* PUT 요청 대신 POST를 사용하며, URL 경로에 update 액션을 명시합니다.
|
||||
*
|
||||
* @param questionId 업데이트할 문항의 고유 ID
|
||||
* @param request Question 업데이트 요청 DTO
|
||||
* @return 업데이트된 문항 정보를 담은 응답 (HTTP 200 OK)
|
||||
* 문항이 없을 경우 HTTP 404 Not Found 반환
|
||||
*/
|
||||
@PutMapping("/{questionId}")
|
||||
@PostMapping("/{questionId}/updateQuestion.do") // POST 요청으로 업데이트 처리, .do 확장자 추가
|
||||
public ResponseEntity<QuestionResponse> updateQuestion(@PathVariable UUID questionId, @RequestBody QuestionUpdateRequest request) {
|
||||
try {
|
||||
QuestionResponse updatedQuestion = QuestionResponse.from(
|
||||
|
|
@ -125,13 +127,14 @@ public class QuestionController {
|
|||
}
|
||||
|
||||
/**
|
||||
* 특정 ID의 문항을 삭제합니다. (HTTP DELETE)
|
||||
* 특정 ID의 문항을 삭제합니다. (HTTP POST)
|
||||
* DELETE 요청 대신 POST를 사용하며, URL 경로에 delete 액션을 명시합니다.
|
||||
*
|
||||
* @param questionId 삭제할 문항의 고유 ID
|
||||
* @return 응답 본문 없이 HTTP 204 No Content 반환
|
||||
* 문항이 없을 경우 HTTP 404 Not Found 반환
|
||||
*/
|
||||
@DeleteMapping("/{questionId}")
|
||||
@PostMapping("/{questionId}/deleteQuestion.do") // POST 요청으로 삭제 처리, .do 확장자 추가
|
||||
public ResponseEntity<Void> deleteQuestion(@PathVariable UUID questionId) {
|
||||
try {
|
||||
questionService.deleteQuestion(questionId);
|
||||
|
|
@ -140,4 +143,4 @@ public class QuestionController {
|
|||
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import java.util.stream.Collectors;
|
|||
/**
|
||||
* 객관식 문항 선택지(QuestionOption) 관련 HTTP 요청을 처리하는 REST 컨트롤러.
|
||||
* 선택지 생성, 조회, 수정, 삭제 등의 API 엔드포인트를 제공합니다.
|
||||
* 모든 요청은 .do 확장자로 끝나며, GET과 POST 메서드만 사용합니다.
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/question-options")
|
||||
|
|
@ -31,7 +32,7 @@ public class QuestionOptionController {
|
|||
* @param request QuestionOption 생성 요청 DTO
|
||||
* @return 생성된 선택지 정보를 담은 응답 (HTTP 201 Created)
|
||||
*/
|
||||
@PostMapping
|
||||
@PostMapping("/createQuestionOption.do") // POST 요청 처리, .do 확장자 추가
|
||||
public ResponseEntity<QuestionOptionResponse> createQuestionOption(@RequestBody QuestionOptionCreateRequest request) {
|
||||
QuestionOptionResponse createdOption = QuestionOptionResponse.from(
|
||||
questionOptionService.createQuestionOption(
|
||||
|
|
@ -49,7 +50,7 @@ public class QuestionOptionController {
|
|||
* @param questionId 선택지 목록을 조회할 문항 ID
|
||||
* @return 해당 문항의 모든 선택지 정보를 담은 응답 리스트 (HTTP 200 OK)
|
||||
*/
|
||||
@GetMapping("/by-question/{questionId}")
|
||||
@GetMapping("/by-question/{questionId}/getOptions.do") // GET 요청 처리, .do 확장자 추가
|
||||
public ResponseEntity<List<QuestionOptionResponse>> getOptionsByQuestionId(@PathVariable UUID questionId) {
|
||||
List<QuestionOptionResponse> options = questionOptionService.getOptionsByQuestionId(questionId).stream()
|
||||
.map(QuestionOptionResponse::from)
|
||||
|
|
@ -64,7 +65,7 @@ public class QuestionOptionController {
|
|||
* @return 조회된 선택지 정보를 담은 응답 (HTTP 200 OK)
|
||||
* 선택지가 없을 경우 HTTP 404 Not Found 반환
|
||||
*/
|
||||
@GetMapping("/{optionId}")
|
||||
@GetMapping("/{optionId}/getOption.do") // GET 요청 처리, .do 확장자 추가
|
||||
public ResponseEntity<QuestionOptionResponse> getQuestionOptionById(@PathVariable UUID optionId) {
|
||||
try {
|
||||
QuestionOptionResponse option = QuestionOptionResponse.from(questionOptionService.getQuestionOptionById(optionId));
|
||||
|
|
@ -75,14 +76,15 @@ public class QuestionOptionController {
|
|||
}
|
||||
|
||||
/**
|
||||
* 선택지 정보를 업데이트합니다. (HTTP PUT)
|
||||
* 선택지 정보를 업데이트합니다. (HTTP POST)
|
||||
* PUT 요청 대신 POST를 사용하며, URL 경로에 update 액션을 명시합니다.
|
||||
*
|
||||
* @param optionId 업데이트할 선택지의 고유 ID
|
||||
* @param request QuestionOption 업데이트 요청 DTO
|
||||
* @return 업데이트된 선택지 정보를 담은 응답 (HTTP 200 OK)
|
||||
* 선택지가 없을 경우 HTTP 404 Not Found 반환
|
||||
*/
|
||||
@PutMapping("/{optionId}")
|
||||
@PostMapping("/{optionId}/updateOption.do") // POST 요청으로 업데이트 처리, .do 확장자 추가
|
||||
public ResponseEntity<QuestionOptionResponse> updateQuestionOption(@PathVariable UUID optionId, @RequestBody QuestionOptionUpdateRequest request) {
|
||||
try {
|
||||
QuestionOptionResponse updatedOption = QuestionOptionResponse.from(
|
||||
|
|
@ -99,13 +101,14 @@ public class QuestionOptionController {
|
|||
}
|
||||
|
||||
/**
|
||||
* 특정 ID의 선택지를 삭제합니다. (HTTP DELETE)
|
||||
* 특정 ID의 선택지를 삭제합니다. (HTTP POST)
|
||||
* DELETE 요청 대신 POST를 사용하며, URL 경로에 delete 액션을 명시합니다.
|
||||
*
|
||||
* @param optionId 삭제할 선택지의 고유 ID
|
||||
* @return 응답 본문 없이 HTTP 204 No Content 반환
|
||||
* 선택지가 없을 경우 HTTP 404 Not Found 반환
|
||||
*/
|
||||
@DeleteMapping("/{optionId}")
|
||||
@PostMapping("/{optionId}/deleteOption.do") // POST 요청으로 삭제 처리, .do 확장자 추가
|
||||
public ResponseEntity<Void> deleteQuestionOption(@PathVariable UUID optionId) {
|
||||
try {
|
||||
questionOptionService.deleteQuestionOption(optionId);
|
||||
|
|
@ -114,4 +117,4 @@ public class QuestionOptionController {
|
|||
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ import org.springframework.http.HttpStatus;
|
|||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.UUID;
|
||||
|
|
@ -33,19 +35,26 @@ public class SurveyController {
|
|||
* @param request Survey 생성 요청 DTO
|
||||
* @return 생성된 설문 정보를 담은 응답 (HTTP 201 Created)
|
||||
*/
|
||||
@PostMapping("/createSurvey.do") // POST 요청 처리, .do 확장자 추가
|
||||
@PostMapping("/createSurvey.do")
|
||||
public ResponseEntity<SurveyResponse> createSurvey(@RequestBody SurveyCreateRequest request) {
|
||||
// 서비스 계층의 createSurvey 메서드를 호출하여 설문을 생성합니다.
|
||||
// DTO의 정보를 서비스 메서드의 파라미터로 전달합니다.
|
||||
|
||||
LocalDateTime startDate = null;
|
||||
if (request.getStartDate() != null && !request.getStartDate().isEmpty()) {
|
||||
startDate = LocalDateTime.parse(request.getStartDate(), DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"));
|
||||
}
|
||||
LocalDateTime endDate = null;
|
||||
if (request.getEndDate() != null && !request.getEndDate().isEmpty()) {
|
||||
endDate = LocalDateTime.parse(request.getEndDate(), DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"));
|
||||
}
|
||||
|
||||
SurveyResponse createdSurvey = SurveyResponse.from(
|
||||
surveyService.createSurvey(
|
||||
request.getSurveyTitle(),
|
||||
request.getSurveyDescription(), // 수정: surveydescription() -> getSurveyDescription()
|
||||
request.getStartDate(),
|
||||
request.getEndDate()
|
||||
request.getSurveyDescription(),
|
||||
startDate, // 파싱된 LocalDateTime 객체 전달
|
||||
endDate // 파싱된 LocalDateTime 객체 전달
|
||||
)
|
||||
);
|
||||
// 생성 성공 시 201 Created 상태 코드와 함께 생성된 설문 정보를 반환합니다.
|
||||
return new ResponseEntity<>(createdSurvey, HttpStatus.CREATED);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ import java.util.UUID;
|
|||
public class SurveyCreateRequest {
|
||||
private String surveyTitle;
|
||||
private String surveyDescription;
|
||||
private LocalDateTime startDate;
|
||||
private LocalDateTime endDate;
|
||||
//private LocalDateTime startDate;
|
||||
//private LocalDateTime endDate;
|
||||
private String startDate;
|
||||
private String endDate;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,16 +2,21 @@ package sgis.surveysystem.mapper;
|
|||
|
||||
import sgis.surveysystem.domain.Question;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Optional; // Optional은 더 이상 findQuestionById의 반환 타입으로 사용하지 않음
|
||||
import java.util.UUID;
|
||||
|
||||
@Mapper
|
||||
public interface QuestionMapper {
|
||||
void insertQuestion(Question question);
|
||||
|
||||
List<Question> findQuestionsBySurveyId(UUID surveyId);
|
||||
Optional<Question> findQuestionById(UUID questionId);
|
||||
|
||||
Question findQuestionById(UUID questionId);
|
||||
|
||||
void updateQuestion(Question question);
|
||||
|
||||
void deleteQuestion(UUID questionId);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,16 +2,20 @@ package sgis.surveysystem.mapper;
|
|||
|
||||
import sgis.surveysystem.domain.QuestionOption;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param; // @Param 사용 시 필요
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Optional; // Optional은 더 이상 findQuestionOptionById의 반환 타입으로 사용하지 않음
|
||||
import java.util.UUID;
|
||||
|
||||
@Mapper
|
||||
public interface QuestionOptionMapper {
|
||||
void insertQuestionOption(QuestionOption option);
|
||||
|
||||
List<QuestionOption> findOptionsByQuestionId(UUID questionId);
|
||||
Optional<QuestionOption> findQuestionOptionById(UUID optionId);
|
||||
QuestionOption findQuestionOptionById(UUID optionId);
|
||||
|
||||
void updateQuestionOption(QuestionOption option);
|
||||
|
||||
void deleteQuestionOption(UUID optionId);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,15 +5,21 @@ import org.apache.ibatis.annotations.Mapper;
|
|||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Optional; // Optional은 더 이상 findSurveyById의 반환 타입으로 사용하지 않음
|
||||
import java.util.UUID;
|
||||
|
||||
@Mapper
|
||||
@Mapper // MyBatis Mapper 임을 선언
|
||||
public interface SurveyMapper {
|
||||
void insertSurvey(Survey survey);
|
||||
List<Survey> findAllSurveys();
|
||||
Optional<Survey> findSurveyById(UUID surveyId);
|
||||
void updateSurvey(Survey survey);
|
||||
void deleteSurvey(UUID surveyId);
|
||||
void updateSurveyStatus(@Param("surveyId") UUID surveyId, @Param("isActive") Boolean isActive);
|
||||
}
|
||||
void insertSurvey(Survey survey); // insert
|
||||
|
||||
List<Survey> findAllSurveys(); // select All
|
||||
|
||||
// findSurveyById 메서드의 반환 타입을 Optional<Survey>에서 Survey로 변경
|
||||
Survey findSurveyById(UUID surveyId); // select by ID (Optional 대신 직접 Survey 반환)
|
||||
|
||||
void updateSurvey(Survey survey); // update
|
||||
|
||||
void deleteSurvey(UUID surveyId); // delete by ID
|
||||
|
||||
void updateSurveyStatus(@Param("surveyId") UUID surveyId, @Param("isActive") Boolean isActive); // 특정 필드 업데이트
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import org.springframework.transaction.annotation.Transactional;
|
|||
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Optional; // Optional 클래스 임포트
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
|
|
@ -71,7 +72,8 @@ public class QuestionOptionService {
|
|||
*/
|
||||
@Transactional(readOnly = true)
|
||||
public QuestionOption getQuestionOptionById(UUID optionId) {
|
||||
return questionOptionMapper.findQuestionOptionById(optionId) // Mapper를 통해 ID로 선택지 조회
|
||||
// Mapper에서 직접 QuestionOption 객체를 반환하도록 변경했으므로, Optional.ofNullable()을 사용합니다.
|
||||
return Optional.ofNullable(questionOptionMapper.findQuestionOptionById(optionId))
|
||||
.orElseThrow(() -> new NoSuchElementException("Question Option not found with ID: " + optionId)); // 없을 경우 예외 발생
|
||||
}
|
||||
|
||||
|
|
@ -109,4 +111,4 @@ public class QuestionOptionService {
|
|||
getQuestionOptionById(optionId); // 삭제할 선택지가 존재하는지 먼저 확인합니다.
|
||||
questionOptionMapper.deleteQuestionOption(optionId); // Mapper를 통해 선택지 삭제
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import org.springframework.transaction.annotation.Transactional;
|
|||
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Optional; // Optional 클래스 임포트
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
|
|
@ -74,7 +75,8 @@ public class QuestionService {
|
|||
*/
|
||||
@Transactional(readOnly = true)
|
||||
public Question getQuestionById(UUID questionId) {
|
||||
return questionMapper.findQuestionById(questionId) // Mapper를 통해 ID로 문항 조회
|
||||
// Mapper에서 직접 Question 객체를 반환하도록 변경했으므로, Optional.ofNullable()을 사용합니다.
|
||||
return Optional.ofNullable(questionMapper.findQuestionById(questionId))
|
||||
.orElseThrow(() -> new NoSuchElementException("Question not found with ID: " + questionId)); // 없을 경우 예외 발생
|
||||
}
|
||||
|
||||
|
|
@ -116,4 +118,4 @@ public class QuestionService {
|
|||
getQuestionById(questionId); // 삭제할 문항이 존재하는지 먼저 확인합니다.
|
||||
questionMapper.deleteQuestion(questionId); // Mapper를 통해 문항 삭제
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import org.springframework.transaction.annotation.Transactional;
|
|||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Optional; // Optional 클래스 임포트
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
|
|
@ -70,7 +71,8 @@ public class SurveyService {
|
|||
*/
|
||||
@Transactional(readOnly = true)
|
||||
public Survey getSurveyById(UUID surveyId) {
|
||||
return surveyMapper.findSurveyById(surveyId) // Mapper를 통해 ID로 설문 조회
|
||||
// Mapper에서 직접 Survey 객체를 반환하도록 변경했으므로, Optional.ofNullable()을 사용합니다.
|
||||
return Optional.ofNullable(surveyMapper.findSurveyById(surveyId))
|
||||
.orElseThrow(() -> new NoSuchElementException("Survey not found with ID: " + surveyId)); // 없을 경우 예외 발생
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -141,6 +141,7 @@
|
|||
</if>
|
||||
<![CDATA[
|
||||
ORDER BY A.PROJECT_CODE desc
|
||||
LIMIT 1
|
||||
]]>
|
||||
</select>
|
||||
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@
|
|||
question_order,
|
||||
is_required
|
||||
) VALUES (
|
||||
#{questionId, jdbcType=OTHER},
|
||||
#{surveyId, jdbcType=OTHER},
|
||||
CAST(#{questionId, jdbcType=OTHER} AS uuid), <!-- 이 부분을 수정했습니다. -->
|
||||
CAST(#{surveyId, jdbcType=OTHER} AS uuid), <!-- 이 부분을 수정했습니다. -->
|
||||
#{questionText},
|
||||
#{questionType},
|
||||
#{questionOrder},
|
||||
|
|
@ -30,7 +30,6 @@
|
|||
)
|
||||
</insert>
|
||||
|
||||
<!-- 설문 ID로 문항 목록 조회 -->
|
||||
<select id="findQuestionsBySurveyId" resultMap="questionResultMap">
|
||||
SELECT
|
||||
question_id,
|
||||
|
|
@ -40,11 +39,10 @@
|
|||
question_order,
|
||||
is_required
|
||||
FROM tb_questions
|
||||
WHERE survey_id = #{surveyId, jdbcType=OTHER}
|
||||
WHERE survey_id = CAST(#{surveyId, jdbcType=OTHER} AS uuid)
|
||||
ORDER BY question_order ASC
|
||||
</select>
|
||||
|
||||
<!-- ID로 특정 문항 조회 -->
|
||||
<select id="findQuestionById" resultMap="questionResultMap">
|
||||
SELECT
|
||||
question_id,
|
||||
|
|
@ -54,10 +52,9 @@
|
|||
question_order,
|
||||
is_required
|
||||
FROM tb_questions
|
||||
WHERE question_id = #{questionId, jdbcType=OTHER}
|
||||
WHERE question_id = CAST(#{questionId, jdbcType=OTHER} AS uuid)
|
||||
</select>
|
||||
|
||||
<!-- 문항 업데이트 -->
|
||||
<update id="updateQuestion" parameterType="sgis.surveysystem.domain.Question">
|
||||
UPDATE tb_questions
|
||||
SET
|
||||
|
|
@ -65,12 +62,11 @@
|
|||
question_type = #{questionType},
|
||||
question_order = #{questionOrder},
|
||||
is_required = #{isRequired}
|
||||
WHERE question_id = #{questionId, jdbcType=OTHER}
|
||||
WHERE question_id = CAST(#{questionId, jdbcType=OTHER} AS uuid)
|
||||
</update>
|
||||
|
||||
<!-- 문항 삭제 -->
|
||||
<delete id="deleteQuestion">
|
||||
DELETE FROM tb_questions
|
||||
WHERE question_id = #{questionId, jdbcType=OTHER}
|
||||
WHERE question_id = CAST(#{questionId, jdbcType=OTHER} AS uuid)
|
||||
</delete>
|
||||
</mapper>
|
||||
|
|
@ -9,6 +9,7 @@
|
|||
<result property="optionOrder" column="option_order"/>
|
||||
</resultMap>
|
||||
|
||||
<!-- 선택지 삽입 -->
|
||||
<insert id="insertQuestionOption" parameterType="sgis.surveysystem.domain.QuestionOption">
|
||||
INSERT INTO tb_question_options (
|
||||
option_id,
|
||||
|
|
@ -16,8 +17,8 @@
|
|||
option_text,
|
||||
option_order
|
||||
) VALUES (
|
||||
#{optionId, jdbcType=OTHER},
|
||||
#{questionId, jdbcType=OTHER},
|
||||
CAST(#{optionId, jdbcType=OTHER} AS uuid), <!-- 이 부분을 수정했습니다. -->
|
||||
CAST(#{questionId, jdbcType=OTHER} AS uuid), <!-- 이 부분을 수정했습니다. -->
|
||||
#{optionText},
|
||||
#{optionOrder}
|
||||
)
|
||||
|
|
@ -30,7 +31,7 @@
|
|||
option_text,
|
||||
option_order
|
||||
FROM tb_question_options
|
||||
WHERE question_id = #{questionId, jdbcType=OTHER}
|
||||
WHERE question_id = CAST(#{questionId, jdbcType=OTHER} AS uuid)
|
||||
ORDER BY option_order ASC
|
||||
</select>
|
||||
|
||||
|
|
@ -41,7 +42,7 @@
|
|||
option_text,
|
||||
option_order
|
||||
FROM tb_question_options
|
||||
WHERE option_id = #{optionId, jdbcType=OTHER}
|
||||
WHERE option_id = CAST(#{optionId, jdbcType=OTHER} AS uuid)
|
||||
</select>
|
||||
|
||||
<update id="updateQuestionOption" parameterType="sgis.surveysystem.domain.QuestionOption">
|
||||
|
|
@ -49,11 +50,11 @@
|
|||
SET
|
||||
option_text = #{optionText},
|
||||
option_order = #{optionOrder}
|
||||
WHERE option_id = #{optionId, jdbcType=OTHER}
|
||||
WHERE option_id = CAST(#{optionId, jdbcType=OTHER} AS uuid)
|
||||
</update>
|
||||
|
||||
<delete id="deleteQuestionOption">
|
||||
DELETE FROM tb_question_options
|
||||
WHERE option_id = #{optionId, jdbcType=OTHER}
|
||||
WHERE option_id = CAST(#{optionId, jdbcType=OTHER} AS uuid)
|
||||
</delete>
|
||||
</mapper>
|
||||
</mapper>
|
||||
|
|
|
|||
|
|
@ -24,7 +24,8 @@
|
|||
start_date,
|
||||
end_date
|
||||
) VALUES (
|
||||
#{surveyId, jdbcType=OTHER}, #{surveyTitle},
|
||||
CAST(#{surveyId, jdbcType=OTHER} AS uuid),
|
||||
#{surveyTitle},
|
||||
#{surveyDescription},
|
||||
#{createdAt},
|
||||
#{updatedAt},
|
||||
|
|
@ -58,7 +59,7 @@
|
|||
start_date,
|
||||
end_date
|
||||
FROM tb_surveys
|
||||
WHERE survey_id = #{surveyId, jdbcType=OTHER}
|
||||
WHERE survey_id = CAST(#{surveyId, jdbcType=OTHER} AS uuid) <!-- 이 부분을 수정했습니다. -->
|
||||
</select>
|
||||
|
||||
<update id="updateSurvey" parameterType="sgis.surveysystem.domain.Survey">
|
||||
|
|
@ -70,7 +71,7 @@
|
|||
is_active = #{isActive},
|
||||
start_date = #{startDate},
|
||||
end_date = #{endDate}
|
||||
WHERE survey_id = #{surveyId, jdbcType=OTHER}
|
||||
WHERE survey_id = CAST(#{surveyId, jdbcType=OTHER} AS uuid) <!-- 이 부분도 일관성을 위해 수정합니다. -->
|
||||
</update>
|
||||
|
||||
<update id="updateSurveyStatus">
|
||||
|
|
@ -78,11 +79,11 @@
|
|||
SET
|
||||
is_active = #{isActive},
|
||||
updated_at = NOW()
|
||||
WHERE survey_id = #{surveyId, jdbcType=OTHER}
|
||||
WHERE survey_id = CAST(#{surveyId, jdbcType=OTHER} AS uuid) <!-- 이 부분도 일관성을 위해 수정합니다. -->
|
||||
</update>
|
||||
|
||||
<delete id="deleteSurvey">
|
||||
DELETE FROM tb_surveys
|
||||
WHERE survey_id = #{surveyId, jdbcType=OTHER}
|
||||
WHERE survey_id = CAST(#{surveyId, jdbcType=OTHER} AS uuid) <!-- 이 부분도 일관성을 위해 수정합니다. -->
|
||||
</delete>
|
||||
</mapper>
|
||||
|
|
@ -4,19 +4,21 @@
|
|||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xmlns:mvc="http://www.springframework.org/schema/mvc"
|
||||
xmlns:task="http://www.springframework.org/schema/task"
|
||||
xmlns:util="http://www.springframework.org/schema/util"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
|
||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
|
||||
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
|
||||
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.0.xsd">
|
||||
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.0.xsd
|
||||
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">
|
||||
|
||||
<!-- 패키지 내 Controller, Service, Repository 클래스의 auto detect를 위한 mvc 설정 -->
|
||||
<context:component-scan base-package="egovframework,sgis">
|
||||
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
|
||||
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
|
||||
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
|
||||
<context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
|
||||
<context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
|
||||
</context:component-scan>
|
||||
|
||||
<!-- 서블릿컨네이너상의 exception에 대한 오류 페이지를 연결하는 mvc 설정-->
|
||||
<!-- 서블릿컨테이너상의 exception에 대한 오류 페이지를 연결하는 mvc 설정-->
|
||||
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
|
||||
<property name="defaultErrorView" value="sgis/com/error/error"/>
|
||||
<property name="exceptionMappings">
|
||||
|
|
@ -39,18 +41,17 @@
|
|||
<!-- 로그인 체크가 필요한 URL과 로그인 여부를 체크해준다 -->
|
||||
<mvc:interceptors>
|
||||
<mvc:interceptor>
|
||||
<mvc:mapping path="/com/loginAction.do"/><!-- 로그인 -->
|
||||
<mvc:mapping path="/com/logoutAction.do"/><!-- 로그아웃 -->
|
||||
<mvc:mapping path="/com/loginAction.do"/>
|
||||
<mvc:mapping path="/com/logoutAction.do"/>
|
||||
<mvc:exclude-mapping path="/com/loginForm.do"/>
|
||||
<bean class="egovframework.com.cmm.interceptor.LoginInterceptor" />
|
||||
</mvc:interceptor>
|
||||
|
||||
<mvc:interceptor>
|
||||
<mvc:mapping path="/sgis/portal/portalMain.do"/>
|
||||
<mvc:mapping path="/com/*.do"/> <!-- 공통 -->
|
||||
<mvc:mapping path="/app/*/*.do"/> <!-- 입력시스템 -->
|
||||
<mvc:mapping path="/map/*.do"/> <!-- 지도시스템 -->
|
||||
<!-- <bean class="egovframework.com.cmm.interceptor.noLoginInterceptor" /> -->
|
||||
<mvc:mapping path="/com/*.do"/>
|
||||
<mvc:mapping path="/app/*/*.do"/>
|
||||
<mvc:mapping path="/map/*.do"/>
|
||||
<bean class="egovframework.com.cmm.interceptor.AuthenticInterceptor" />
|
||||
</mvc:interceptor>
|
||||
</mvc:interceptors>
|
||||
|
|
@ -58,6 +59,41 @@
|
|||
<!-- Annotation 을 사용하지 않는 경우에 대한 MVC 처리 설정 -->
|
||||
<mvc:view-controller path="/cmmn/validator.do" view-name="cmmn/validator"/>
|
||||
|
||||
<mvc:annotation-driven/>
|
||||
<!-- ObjectMapper 빈을 전역적으로 정의 -->
|
||||
<bean id="objectMapper" class="com.fasterxml.jackson.databind.ObjectMapper">
|
||||
<!-- 기본 생성자 사용 -->
|
||||
<!-- 날짜/시간 포맷팅 설정 (선택 사항이지만 권장) -->
|
||||
<property name="serializationInclusion" value="NON_NULL"/>
|
||||
<property name="dateFormat">
|
||||
<bean class="com.fasterxml.jackson.databind.util.StdDateFormat"/>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
<!-- ObjectMapper에 JavaTimeModule 등록을 위한 MethodInvokingFactoryBean -->
|
||||
<!-- objectMapper 빈이 생성된 후 registerModule 메서드를 호출하여 모듈을 등록합니다. -->
|
||||
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
|
||||
<property name="targetObject" ref="objectMapper"/>
|
||||
<property name="targetMethod" value="registerModule"/>
|
||||
<property name="arguments">
|
||||
<list>
|
||||
<bean class="com.fasterxml.jackson.datatype.jsr310.JavaTimeModule"/>
|
||||
</list>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<!-- mvc:annotation-driven을 명시적으로 재정의하여 메시지 컨버터 설정 -->
|
||||
<mvc:annotation-driven>
|
||||
<mvc:message-converters>
|
||||
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
|
||||
<property name="supportedMediaTypes">
|
||||
<list>
|
||||
<value>application/json;charset=UTF-8</value>
|
||||
</list>
|
||||
</property>
|
||||
<!-- 전역으로 정의된 objectMapper 빈을 참조 -->
|
||||
<property name="objectMapper" ref="objectMapper"/>
|
||||
</bean>
|
||||
</mvc:message-converters>
|
||||
</mvc:annotation-driven>
|
||||
|
||||
</beans>
|
||||
|
|
|
|||
|
|
@ -216,6 +216,7 @@ function selectComboList(class_name, data, selected_data){
|
|||
}
|
||||
|
||||
function go_save(){
|
||||
debugger;
|
||||
<c:if test="${empty fileInfo}">
|
||||
var upload = $("#fileUpload1").data("kendoUpload");
|
||||
var upCnt = upload.getFiles().length
|
||||
|
|
|
|||
|
|
@ -888,7 +888,7 @@
|
|||
</div>
|
||||
|
||||
<div class="title">
|
||||
지반주상도<span class="borehole-coordinate-system-info" id="borehole-coordinate-system-info" ></span>
|
||||
시추주상도<span class="borehole-coordinate-system-info" id="borehole-coordinate-system-info" ></span>
|
||||
</div>
|
||||
<table class="header" id="meta-data">
|
||||
<thead>
|
||||
|
|
|
|||
|
|
@ -186,7 +186,7 @@
|
|||
<ul>
|
||||
<li class="option-mouse"><span class="option-title">이동</span>마우스 왼쪽 드래그, <span class="option-title">확대</span>L-Shift + 마우스 왼쪽 드래그</li>
|
||||
<li class="option-ex ex-sample">시료를 채취한 부분으로 실내시험 결과를 확인 할 수 있습니다.</li>
|
||||
<li class="option-ex ex-jusang">지반주상도 정보를 확인 하실 수 있습니다.</li>
|
||||
<li class="option-ex ex-jusang">시추주상도 정보를 확인 하실 수 있습니다.</li>
|
||||
<li class="option-ex ex-info">지반단면도에 대한 XML 데이터를 보실 수 있습니다.</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -381,7 +381,7 @@
|
|||
</li>
|
||||
<li style="width: 400px;">
|
||||
<a href="javascript:showSichudan();" id="borehole-profile" class="btn btn-ske-blue btn-icon-left btn-icon-search" autocomplete="off" ><span>지반단면도</span></a>
|
||||
<a href="javascript:showBoreholeLog();" id="borehole-log" class="btn btn-ske-blue btn-icon-left btn-icon-search" autocomplete="off" ><span>지반주상도</span></a>
|
||||
<a href="javascript:showBoreholeLog();" id="borehole-log" class="btn btn-ske-blue btn-icon-left btn-icon-search" autocomplete="off" ><span>시추주상도</span></a>
|
||||
<script>
|
||||
|
||||
function showSichudan() {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,441 @@
|
|||
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
|
||||
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>
|
||||
<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>설문 문항 생성 (관리자)</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Malgun Gothic', '맑은 고딕', Arial, sans-serif;
|
||||
margin: 20px;
|
||||
background-color: #f4f4f4;
|
||||
color: #333;
|
||||
display: flex;
|
||||
flex-direction: column; /* 세로 정렬 */
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
}
|
||||
.header-section {
|
||||
background-color: #fff;
|
||||
padding: 20px 25px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
max-width: 600px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 20px; /* 폼과의 간격 */
|
||||
}
|
||||
.container {
|
||||
background-color: #fff;
|
||||
padding: 25px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
max-width: 600px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
h2 {
|
||||
color: #0056b3;
|
||||
text-align: center;
|
||||
margin-bottom: 25px;
|
||||
font-size: 1.8em;
|
||||
border-bottom: 2px solid #eee;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
.form-group {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 6px;
|
||||
font-weight: bold;
|
||||
color: #555;
|
||||
}
|
||||
.form-group input[type="text"],
|
||||
.form-group input[type="number"],
|
||||
.form-group select,
|
||||
.form-group textarea {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 5px;
|
||||
box-sizing: border-box;
|
||||
font-size: 1em;
|
||||
transition: border-color 0.3s ease;
|
||||
}
|
||||
.form-group input[type="text"]:focus,
|
||||
.form-group input[type="number"]:focus,
|
||||
.form-group select:focus,
|
||||
.form-group input[type="datetime-local"]:focus,
|
||||
.form-group textarea:focus {
|
||||
border-color: #007bff;
|
||||
outline: none;
|
||||
}
|
||||
.form-group textarea {
|
||||
resize: vertical;
|
||||
min-height: 80px;
|
||||
}
|
||||
.form-group input[type="checkbox"] {
|
||||
margin-right: 5px;
|
||||
}
|
||||
button {
|
||||
background-color: #28a745; /* Green for create */
|
||||
color: white;
|
||||
padding: 12px 20px;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
font-size: 1.1em;
|
||||
width: 100%;
|
||||
transition: background-color 0.3s ease, transform 0.2s ease;
|
||||
margin-top: 10px;
|
||||
}
|
||||
button:hover {
|
||||
background-color: #218838;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
button:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
.message {
|
||||
margin-top: 25px;
|
||||
padding: 12px;
|
||||
border-radius: 5px;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
display: none;
|
||||
word-break: break-all;
|
||||
}
|
||||
.message.success {
|
||||
background-color: #d4edda;
|
||||
color: #155724;
|
||||
border: 1px solid #c3e6cb;
|
||||
}
|
||||
.message.error {
|
||||
background-color: #f8d7da;
|
||||
color: #721c24;
|
||||
border: 1px solid #f5c6cb;
|
||||
}
|
||||
.back-link {
|
||||
display: block;
|
||||
text-align: center;
|
||||
margin-top: 20px;
|
||||
color: #007bff;
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
}
|
||||
.back-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* 기존 문항 목록 스타일 */
|
||||
.question-list-section {
|
||||
margin-top: 30px;
|
||||
background-color: #fff;
|
||||
padding: 25px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
max-width: 600px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.question-list-section h3 {
|
||||
color: #0056b3;
|
||||
margin-bottom: 15px;
|
||||
border-bottom: 1px dashed #eee;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
.question-item {
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 5px;
|
||||
padding: 15px;
|
||||
margin-bottom: 10px;
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
.question-item p {
|
||||
margin: 0 0 5px 0;
|
||||
font-weight: bold;
|
||||
color: #444;
|
||||
}
|
||||
.question-item small {
|
||||
color: #777;
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.question-options {
|
||||
margin-top: 10px;
|
||||
padding-left: 20px;
|
||||
border-left: 2px solid #ddd;
|
||||
}
|
||||
.question-options li {
|
||||
margin-bottom: 5px;
|
||||
color: #666;
|
||||
}
|
||||
.question-options li:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.no-questions {
|
||||
text-align: center;
|
||||
color: #888;
|
||||
padding: 20px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header-section">
|
||||
<h2>설문 ID: <span id="displaySurveyId">${surveyId}</span></h2>
|
||||
<p>이 설문에 대한 문항들을 생성합니다.</p>
|
||||
<div id="initialMessage" class="message"></div>
|
||||
</div>
|
||||
|
||||
<div class="question-list-section">
|
||||
<h3>기존 문항 목록</h3>
|
||||
<div id="existingQuestionsList">
|
||||
<p class="no-questions">로딩 중...</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<h2>새 문항 생성</h2>
|
||||
<form id="createQuestionForm">
|
||||
<!-- Hidden input to pass surveyId from URL to form data -->
|
||||
<input type="hidden" id="surveyId" name="surveyId" value="${surveyId}">
|
||||
|
||||
<div class="form-group">
|
||||
<label for="questionText">문항 내용:</label>
|
||||
<textarea id="questionText" name="questionText" placeholder="예: 시스템 사용에 만족하십니까?" required></textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="questionType">문항 유형:</label>
|
||||
<select id="questionType" name="questionType" required>
|
||||
<option value="">-- 선택 --</option>
|
||||
<option value="단답형">단답형</option>
|
||||
<option value="객관식_단일">객관식 (단일 선택)</option>
|
||||
<option value="객관식_다중">객관식 (다중 선택)</option>
|
||||
<option value="주관식">주관식</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="questionOrder">문항 순서:</label>
|
||||
<input type="number" id="questionOrder" name="questionOrder" value="1" min="1" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="checkbox" id="isRequired" name="isRequired" checked>
|
||||
<label for="isRequired">필수 응답</label>
|
||||
</div>
|
||||
|
||||
<button type="submit">문항 생성</button>
|
||||
</form>
|
||||
<div id="message" class="message"></div>
|
||||
<a href="#" class="back-link" onclick="history.back(); return false;">← 이전 페이지로 돌아가기</a>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const contextPath = "<%= request.getContextPath() %>";
|
||||
const surveyId = document.getElementById('surveyId').value;
|
||||
const initialMessageDiv = document.getElementById('initialMessage');
|
||||
const existingQuestionsListDiv = document.getElementById('existingQuestionsList');
|
||||
|
||||
// Function to fetch and display existing questions
|
||||
async function fetchAndDisplayQuestions() {
|
||||
console.log('fetchAndDisplayQuestions called for surveyId:', surveyId); // Debugging
|
||||
if (!surveyId || surveyId === 'null' || surveyId === '') {
|
||||
existingQuestionsListDiv.innerHTML = '<p class="no-questions">설문 ID가 유효하지 않아 기존 문항을 불러올 수 없습니다.</p>';
|
||||
console.log('Invalid surveyId, not fetching questions.'); // Debugging
|
||||
return;
|
||||
}
|
||||
|
||||
const apiUrl = (typeof contextPath !== 'undefined' && contextPath !== null && contextPath !== '') ?
|
||||
contextPath + '/api/questions/by-survey/' + surveyId + '/getQuestions.do' :
|
||||
'/api/questions/by-survey/' + surveyId + '/getQuestions.do';
|
||||
|
||||
console.log('Fetching questions from:', apiUrl); // Debugging
|
||||
try {
|
||||
const response = await fetch(apiUrl);
|
||||
console.log('Questions API Response Status:', response.status, response.statusText); // Debugging
|
||||
if (response.ok) {
|
||||
const questions = await response.json();
|
||||
console.log('Fetched Questions (RAW):', JSON.stringify(questions, null, 2)); // Debugging: Raw JSON data
|
||||
|
||||
let maxOrder = 0; // 최대 문항 순서를 추적하기 위한 변수
|
||||
|
||||
if (questions.length === 0) {
|
||||
existingQuestionsListDiv.innerHTML = '<p class="no-questions">아직 생성된 문항이 없습니다.</p>';
|
||||
maxOrder = 0; // 문항이 없으면 최대 순서는 0
|
||||
} else {
|
||||
existingQuestionsListDiv.innerHTML = ''; // Clear loading message
|
||||
for (const question of questions) {
|
||||
// 현재 문항의 순서가 maxOrder보다 크면 업데이트
|
||||
if (question.questionOrder > maxOrder) {
|
||||
maxOrder = question.questionOrder;
|
||||
}
|
||||
|
||||
console.log('Processing question:', JSON.stringify(question, null, 2)); // Debugging: Each question object
|
||||
const questionItem = document.createElement('div');
|
||||
questionItem.classList.add('question-item');
|
||||
|
||||
// 문항 클릭 시 선택지 생성 페이지로 이동하는 이벤트 리스너 추가
|
||||
questionItem.style.cursor = 'pointer'; // 클릭 가능한 UI임을 나타내기 위해 커서 스타일 변경
|
||||
questionItem.addEventListener('click', function() {
|
||||
const redirectUrl = (typeof contextPath !== 'undefined' && contextPath !== null && contextPath !== '') ?
|
||||
contextPath + '/admin/survey/question/' + question.questionId + '/createOptionForm.do' :
|
||||
'/admin/survey/question/' + question.questionId + '/createOptionForm.do';
|
||||
window.location.href = redirectUrl;
|
||||
});
|
||||
|
||||
let optionsHtml = '';
|
||||
// questionType이 객관식이고, question.options 배열이 존재하는 경우에만 옵션 처리
|
||||
if (question.questionType && question.questionType.startsWith('객관식') && question.options && question.options.length > 0) {
|
||||
optionsHtml = '<ul class="question-options">';
|
||||
question.options.forEach(option => {
|
||||
// 옵션 데이터도 안전하게 접근
|
||||
const optionOrder = option.optionOrder !== null && option.optionOrder !== undefined ? option.optionOrder : '';
|
||||
const optionText = option.optionText !== null && option.optionText !== undefined ? option.optionText : '';
|
||||
// 백틱(`) 대신 문자열 연결(+) 사용
|
||||
optionsHtml += '<li>' + optionOrder + '. ' + optionText + '</li>';
|
||||
});
|
||||
optionsHtml += '</ul>';
|
||||
} else if (question.questionType && question.questionType.startsWith('객관식')) {
|
||||
// 객관식 타입이지만 옵션이 없는 경우
|
||||
optionsHtml = '<p class="question-options"><em>(선택지 없음)</em></p>';
|
||||
}
|
||||
// 주관식 등 옵션이 필요 없는 경우 optionsHtml은 빈 문자열로 유지됩니다.
|
||||
|
||||
// 안전하게 값 접근 및 플레이스홀더 설정
|
||||
const order = String(question.questionOrder || '[순서 없음]');
|
||||
const text = String(question.questionText || '[내용 없음]');
|
||||
const type = String(question.questionType || '[유형 없음]');
|
||||
// isRequired는 boolean 타입이므로, 명시적으로 '예'/'아니오'로 변환
|
||||
const required = (question.isRequired === true) ? '예' : '아니오';
|
||||
|
||||
console.log('Final values for HTML:', { order, text, type, required }); // Add this line
|
||||
|
||||
// 백틱(`) 대신 문자열 연결(+) 사용
|
||||
questionItem.innerHTML =
|
||||
'<p>' + order + '. ' + text + '</p>' +
|
||||
'<small>유형: ' + type + ' | 필수: ' + required + '</small>' +
|
||||
optionsHtml;
|
||||
|
||||
existingQuestionsListDiv.appendChild(questionItem);
|
||||
console.log('Appended question item:', questionItem.outerHTML); // Debugging: Check generated HTML
|
||||
}
|
||||
}
|
||||
|
||||
// 모든 문항 처리 후, 다음 문항 순서 설정
|
||||
// 기존 문항이 없으면 1, 있으면 최대 순서 + 1
|
||||
document.getElementById('questionOrder').value = maxOrder + 1;
|
||||
|
||||
} else {
|
||||
existingQuestionsListDiv.innerHTML = '<p class="no-questions error">기존 문항을 불러오는데 실패했습니다.</p>';
|
||||
console.error('Failed to fetch questions. Status:', response.status, response.statusText); // Debugging
|
||||
// 오류 발생 시에도 기본값은 1로 설정 (혹은 기존 HTML 기본값 유지)
|
||||
document.getElementById('questionOrder').value = 1;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching questions:', error);
|
||||
existingQuestionsListDiv.innerHTML = '<p class="no-questions error">네트워크 오류로 문항을 불러올 수 없습니다.</p>';
|
||||
// 네트워크 오류 발생 시에도 기본값은 1로 설정
|
||||
document.getElementById('questionOrder').value = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// --- Event Listener for Form Submission ---
|
||||
document.getElementById('createQuestionForm').addEventListener('submit', async function(event) {
|
||||
event.preventDefault(); // 폼의 기본 제출 동작 방지
|
||||
|
||||
const messageDiv = document.getElementById('message');
|
||||
messageDiv.style.display = 'none'; // 이전 메시지 숨기기
|
||||
messageDiv.classList.remove('success', 'error'); // 이전 스타일 클래스 제거
|
||||
|
||||
// 폼 필드에서 데이터 수집
|
||||
const formData = {
|
||||
surveyId: surveyId, // Hidden input에서 surveyId 가져오기
|
||||
questionText: document.getElementById('questionText').value,
|
||||
questionType: document.getElementById('questionType').value,
|
||||
questionOrder: parseInt(document.getElementById('questionOrder').value, 10), // 숫자로 파싱
|
||||
isRequired: document.getElementById('isRequired').checked // 체크박스 값
|
||||
};
|
||||
|
||||
// 유효성 검사 (간단한 예시)
|
||||
if (!formData.surveyId || formData.surveyId === 'null' || formData.surveyId === '') { // 빈 문자열도 확인
|
||||
messageDiv.textContent = '설문 ID가 유효하지 않습니다. 이전 페이지에서 설문을 선택해주세요.';
|
||||
messageDiv.classList.add('error');
|
||||
messageDiv.style.display = 'block';
|
||||
return;
|
||||
}
|
||||
if (!formData.questionType) {
|
||||
messageDiv.textContent = '문항 유형을 선택해주세요.';
|
||||
messageDiv.classList.add('error');
|
||||
messageDiv.style.display = 'block';
|
||||
return;
|
||||
}
|
||||
|
||||
// QuestionController의 @RequestMapping("/api/questions")와 @PostMapping("/createQuestion.do")를 결합
|
||||
const apiUrl = (typeof contextPath !== 'undefined' && contextPath !== null && contextPath !== '') ? contextPath + '/api/questions/createQuestion.do' : '/api/questions/createQuestion.do';
|
||||
|
||||
try {
|
||||
const response = await fetch(apiUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(formData) // JSON 문자열로 변환하여 전송
|
||||
});
|
||||
|
||||
if (response.ok) { // HTTP 상태 코드가 200-299 범위인 경우 (성공)
|
||||
const result = await response.json(); // 응답 본문을 JSON으로 파싱
|
||||
messageDiv.textContent = '문항이 성공적으로 생성되었습니다! (ID: ' + result.questionId + ')';
|
||||
messageDiv.classList.add('success');
|
||||
messageDiv.style.display = 'block';
|
||||
|
||||
// 폼 초기화 (surveyId는 유지)
|
||||
document.getElementById('questionText').value = '';
|
||||
document.getElementById('questionType').value = '';
|
||||
document.getElementById('questionOrder').value = parseInt(formData.questionOrder, 10) + 1; // 다음 문항 순서 자동 증가
|
||||
document.getElementById('isRequired').checked = true;
|
||||
|
||||
// 문항 생성 성공 후 기존 문항 목록 새로고침
|
||||
await fetchAndDisplayQuestions(); // 목록 새로고침
|
||||
|
||||
// 문항 유형이 객관식인 경우, 선택지 생성 페이지로 이동
|
||||
if (formData.questionType.startsWith('객관식')) {
|
||||
const redirectUrl = (typeof contextPath !== 'undefined' && contextPath !== null && contextPath !== '') ?
|
||||
contextPath + '/admin/survey/question/' + result.questionId + '/createOptionForm.do' :
|
||||
'/admin/survey/question/' + result.questionId + '/createOptionForm.do';
|
||||
window.location.href = redirectUrl; // 페이지 리다이렉트
|
||||
}
|
||||
|
||||
} else { // HTTP 상태 코드가 200-299 범위를 벗어난 경우 (실패)
|
||||
const errorText = await response.text(); // 오류 메시지를 텍스트로 가져옴
|
||||
messageDiv.textContent = '문항 생성 실패: ' + (errorText || '알 수 없는 오류');
|
||||
messageDiv.classList.add('error');
|
||||
messageDiv.style.display = 'block';
|
||||
}
|
||||
} catch (error) {
|
||||
// 네트워크 오류 또는 fetch 요청 자체의 문제 발생 시
|
||||
console.error('네트워크 오류:', error);
|
||||
messageDiv.textContent = '네트워크 오류가 발생했습니다. 다시 시도해주세요.';
|
||||
messageDiv.classList.add('error');
|
||||
messageDiv.style.display = 'block';
|
||||
}
|
||||
});
|
||||
|
||||
// --- Initial Load Logic ---
|
||||
// 초기 로드 시 surveyId가 없으면 경고 메시지 표시
|
||||
if (!surveyId || surveyId === 'null' || surveyId === '') {
|
||||
initialMessageDiv.textContent = '경고: 설문 ID가 없습니다. 문항을 생성할 설문을 먼저 선택해주세요.';
|
||||
initialMessageDiv.classList.add('error');
|
||||
initialMessageDiv.style.display = 'block';
|
||||
existingQuestionsListDiv.innerHTML = '<p class="no-questions">설문 ID가 없어 문항 목록을 불러올 수 없습니다.</p>';
|
||||
} else {
|
||||
// surveyId가 유효하면 기존 문항 목록을 불러옵니다.
|
||||
fetchAndDisplayQuestions();
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,428 @@
|
|||
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
|
||||
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>
|
||||
<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>문항 선택지 생성 (관리자)</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Malgun Gothic', '맑은 고딕', Arial, sans-serif;
|
||||
margin: 20px;
|
||||
background-color: #f4f4f4;
|
||||
color: #333;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
}
|
||||
.header-section {
|
||||
background-color: #fff;
|
||||
padding: 20px 25px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
max-width: 600px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.container {
|
||||
background-color: #fff;
|
||||
padding: 25px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
max-width: 600px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
h2 {
|
||||
color: #0056b3;
|
||||
text-align: center;
|
||||
margin-bottom: 25px;
|
||||
font-size: 1.8em;
|
||||
border-bottom: 2px solid #eee;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
.form-group {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 6px;
|
||||
font-weight: bold;
|
||||
color: #555;
|
||||
}
|
||||
.form-group input[type="text"],
|
||||
.form-group input[type="number"],
|
||||
.form-group select,
|
||||
.form-group textarea {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 5px;
|
||||
box-sizing: border-box;
|
||||
font-size: 1em;
|
||||
transition: border-color 0.3s ease;
|
||||
}
|
||||
.form-group input[type="text"]:focus,
|
||||
.form-group input[type="number"]:focus,
|
||||
.form-group select:focus,
|
||||
.form-group input[type="datetime-local"]:focus,
|
||||
.form-group textarea:focus {
|
||||
border-color: #007bff;
|
||||
outline: none;
|
||||
}
|
||||
.form-group textarea {
|
||||
resize: vertical;
|
||||
min-height: 80px;
|
||||
}
|
||||
.form-group input[type="checkbox"] {
|
||||
margin-right: 5px;
|
||||
}
|
||||
button {
|
||||
background-color: #28a745;
|
||||
color: white;
|
||||
padding: 12px 20px;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
font-size: 1.1em;
|
||||
width: 100%;
|
||||
transition: background-color 0.3s ease, transform 0.2s ease;
|
||||
margin-top: 10px;
|
||||
}
|
||||
button:hover {
|
||||
background-color: #218838;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
button:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
.message {
|
||||
margin-top: 25px;
|
||||
padding: 12px;
|
||||
border-radius: 5px;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
display: none;
|
||||
word-break: break-all;
|
||||
}
|
||||
.message.success {
|
||||
background-color: #d4edda;
|
||||
color: #155724;
|
||||
border: 1px solid #c3e6cb;
|
||||
}
|
||||
.message.error {
|
||||
background-color: #f8d7da;
|
||||
color: #721c24;
|
||||
border: 1px solid #f5c6cb;
|
||||
}
|
||||
.back-link {
|
||||
display: block;
|
||||
text-align: center;
|
||||
margin-top: 20px;
|
||||
color: #007bff;
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
}
|
||||
.back-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Existing options list styles */
|
||||
.option-list-section {
|
||||
margin-top: 30px;
|
||||
background-color: #fff;
|
||||
padding: 25px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
max-width: 600px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.option-list-section h3 {
|
||||
color: #0056b3;
|
||||
margin-bottom: 15px;
|
||||
border-bottom: 1px dashed #eee;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
.option-item {
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 5px;
|
||||
padding: 15px;
|
||||
margin-bottom: 10px;
|
||||
background-color: #f9f9f9;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.option-item p {
|
||||
margin: 0;
|
||||
font-weight: bold;
|
||||
color: #444;
|
||||
flex-grow: 1;
|
||||
}
|
||||
.option-item small {
|
||||
color: #777;
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.no-options {
|
||||
text-align: center;
|
||||
color: #888;
|
||||
padding: 20px;
|
||||
}
|
||||
.delete-option-btn {
|
||||
background-color: #dc3545;
|
||||
color: white;
|
||||
padding: 5px 10px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 0.8em;
|
||||
margin-left: 10px;
|
||||
transition: background-color 0.3s ease;
|
||||
width: auto;
|
||||
}
|
||||
.delete-option-btn:hover {
|
||||
background-color: #c82333;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header-section">
|
||||
<h2>문항 ID: <span id="displayQuestionId"><%= request.getAttribute("questionId") %></span></h2>
|
||||
<p>이 문항에 대한 선택지들을 생성합니다.</p>
|
||||
<div id="initialMessage" class="message"></div>
|
||||
</div>
|
||||
|
||||
<div class="option-list-section">
|
||||
<h3>기존 선택지 목록</h3>
|
||||
<div id="existingOptionsList">
|
||||
<p class="no-options">로딩 중...</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<h2>새 선택지 생성</h2>
|
||||
<form id="createOptionForm">
|
||||
<!-- Hidden input to pass questionId from URL to form data -->
|
||||
<input type="hidden" id="questionId" name="questionId" value="<%= request.getAttribute("questionId") %>">
|
||||
|
||||
<div class="form-group">
|
||||
<label for="optionText">선택지 내용:</label>
|
||||
<input type="text" id="optionText" name="optionText" placeholder="예: 매우 만족" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="optionOrder">선택지 순서:</label>
|
||||
<input type="number" id="optionOrder" name="optionOrder" value="1" min="1" required>
|
||||
</div>
|
||||
|
||||
<button type="submit">선택지 생성</button>
|
||||
</form>
|
||||
<div id="message" class="message"></div>
|
||||
<a href="#" class="back-link" onclick="history.back(); return false;">← 이전 페이지로 돌아가기</a>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const contextPath = "<%= request.getContextPath() %>";
|
||||
const questionId = document.getElementById('questionId').value;
|
||||
const initialMessageDiv = document.getElementById('initialMessage');
|
||||
const existingOptionsListDiv = document.getElementById('existingOptionsList');
|
||||
const messageDiv = document.getElementById('message'); // 메시지 div 전역 변수로 선언
|
||||
|
||||
// Function to fetch and display existing options
|
||||
async function fetchAndDisplayOptions() {
|
||||
console.log('fetchAndDisplayOptions called for questionId:', questionId);
|
||||
if (!questionId || questionId === 'null' || questionId === '') {
|
||||
existingOptionsListDiv.innerHTML = '<p class="no-options">문항 ID가 유효하지 않아 기존 선택지를 불러올 수 없습니다.</p>';
|
||||
console.log('Invalid questionId, not fetching options.');
|
||||
return;
|
||||
}
|
||||
|
||||
const apiUrl = (typeof contextPath !== 'undefined' && contextPath !== null && contextPath !== '') ?
|
||||
contextPath + '/api/question-options/by-question/' + questionId + '/getOptions.do' :
|
||||
'/api/question-options/by-question/' + questionId + '/getOptions.do';
|
||||
|
||||
console.log('Fetching options from:', apiUrl);
|
||||
try {
|
||||
const response = await fetch(apiUrl);
|
||||
console.log('Options API Response Status:', response.status, response.statusText);
|
||||
if (response.ok) {
|
||||
const options = await response.json();
|
||||
console.log('Fetched Options (RAW):', JSON.stringify(options, null, 2));
|
||||
|
||||
let maxOrder = 0;
|
||||
|
||||
if (options.length === 0) {
|
||||
existingOptionsListDiv.innerHTML = '<p class="no-options">아직 생성된 선택지가 없습니다.</p>';
|
||||
maxOrder = 0;
|
||||
} else {
|
||||
existingOptionsListDiv.innerHTML = '';
|
||||
for (const option of options) {
|
||||
const currentOrder = parseInt(option.optionOrder, 10);
|
||||
if (!isNaN(currentOrder) && currentOrder > maxOrder) {
|
||||
maxOrder = currentOrder;
|
||||
}
|
||||
|
||||
console.log('Processing option:', JSON.stringify(option, null, 2));
|
||||
const optionItem = document.createElement('div');
|
||||
optionItem.classList.add('option-item');
|
||||
|
||||
const order = String(option.optionOrder || '[순서 없음]');
|
||||
const text = String(option.optionText || '[내용 없음]');
|
||||
const optionIdValue = String(option.optionId || '');
|
||||
|
||||
console.log('Final values for HTML:', { order, text, optionIdValue });
|
||||
|
||||
optionItem.innerHTML =
|
||||
'<p>' + order + '. ' + text + '</p>' +
|
||||
'<button type="button" class="delete-option-btn" data-option-id="' + optionIdValue + '">삭제</button>';
|
||||
|
||||
existingOptionsListDiv.appendChild(optionItem);
|
||||
console.log('Appended option item:', optionItem.outerHTML);
|
||||
}
|
||||
|
||||
// Add event listeners for delete buttons after all options are appended
|
||||
document.querySelectorAll('.delete-option-btn').forEach(button => {
|
||||
button.addEventListener('click', async function() {
|
||||
const optionIdToDelete = this.dataset.optionId;
|
||||
console.log('Delete button clicked for optionId:', optionIdToDelete);
|
||||
|
||||
if (!confirm('정말로 이 선택지를 삭제하시겠습니까?')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// DELETE 요청 대신 POST를 사용하며, URL 경로에 delete 액션을 명시
|
||||
const deleteApiUrl = (typeof contextPath !== 'undefined' && contextPath !== null && contextPath !== '') ?
|
||||
contextPath + '/api/question-options/' + optionIdToDelete + '/deleteOption.do' :
|
||||
'/api/question-options/' + optionIdToDelete + '/deleteOption.do';
|
||||
|
||||
console.log('Attempting to delete option from:', deleteApiUrl); // Debugging
|
||||
try {
|
||||
const deleteResponse = await fetch(deleteApiUrl, {
|
||||
method: 'POST', // POST로 변경
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
// POST로 삭제 시, PathVariable로 ID를 보내므로 body는 필요 없음
|
||||
// body: JSON.stringify({ optionId: optionIdToDelete }) // 서버가 @RequestBody로 ID를 받는다면 이 줄 활성화
|
||||
});
|
||||
|
||||
console.log('Delete API Response Status:', deleteResponse.status, deleteResponse.statusText); // Debugging
|
||||
if (deleteResponse.ok) { // HTTP 상태 코드가 200-299 범위인 경우 (성공)
|
||||
console.log('Option deleted successfully:', optionIdToDelete);
|
||||
messageDiv.textContent = '선택지가 성공적으로 삭제되었습니다.';
|
||||
messageDiv.classList.add('success');
|
||||
messageDiv.style.display = 'block';
|
||||
await fetchAndDisplayOptions(); // 목록 새로고침
|
||||
} else { // HTTP 상태 코드가 200-299 범위를 벗어난 경우 (실패)
|
||||
const errorText = await deleteResponse.text();
|
||||
console.error('Failed to delete option. Status:', deleteResponse.status, 'Error:', errorText);
|
||||
messageDiv.textContent = '선택지 삭제 실패: ' + (errorText || '알 수 없는 오류');
|
||||
messageDiv.classList.add('error');
|
||||
messageDiv.style.display = 'block';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error deleting option:', error);
|
||||
messageDiv.textContent = '네트워크 오류로 선택지를 삭제할 수 없습니다.';
|
||||
messageDiv.classList.add('error');
|
||||
messageDiv.style.display = 'block';
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
document.getElementById('optionOrder').value = maxOrder + 1;
|
||||
|
||||
} else {
|
||||
existingOptionsListDiv.innerHTML = '<p class="no-options error">기존 선택지를 불러오는데 실패했습니다.</p>';
|
||||
console.error('Failed to fetch options. Status:', response.status, response.statusText);
|
||||
document.getElementById('optionOrder').value = 1;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching options:', error);
|
||||
existingOptionsListDiv.innerHTML = '<p class="no-options error">네트워크 오류로 선택지를 불러올 수 없습니다.</p>';
|
||||
document.getElementById('optionOrder').value = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// --- Event Listener for Form Submission ---
|
||||
document.getElementById('createOptionForm').addEventListener('submit', async function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
const messageDiv = document.getElementById('message');
|
||||
messageDiv.style.display = 'none';
|
||||
messageDiv.classList.remove('success', 'error');
|
||||
|
||||
const formData = {
|
||||
questionId: questionId,
|
||||
optionText: document.getElementById('optionText').value,
|
||||
optionOrder: parseInt(document.getElementById('optionOrder').value, 10)
|
||||
};
|
||||
|
||||
if (!formData.questionId || formData.questionId === 'null' || formData.questionId === '') {
|
||||
messageDiv.textContent = '문항 ID가 유효하지 않습니다. 이전 페이지에서 문항을 선택해주세요.';
|
||||
messageDiv.classList.add('error');
|
||||
messageDiv.style.display = 'block';
|
||||
return;
|
||||
}
|
||||
if (!formData.optionText.trim()) {
|
||||
messageDiv.textContent = '선택지 내용을 입력해주세요.';
|
||||
messageDiv.classList.add('error');
|
||||
messageDiv.style.display = 'block';
|
||||
return;
|
||||
}
|
||||
|
||||
const apiUrl = (typeof contextPath !== 'undefined' && contextPath !== null && contextPath !== '') ? contextPath + '/api/question-options/createQuestionOption.do' : '/api/question-options/createQuestionOption.do';
|
||||
|
||||
try {
|
||||
const response = await fetch(apiUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(formData)
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const result = await response.json();
|
||||
messageDiv.textContent = '선택지가 성공적으로 생성되었습니다! (ID: ' + result.optionId + ')';
|
||||
messageDiv.classList.add('success');
|
||||
messageDiv.style.display = 'block';
|
||||
|
||||
document.getElementById('optionText').value = '';
|
||||
document.getElementById('optionOrder').value = parseInt(formData.optionOrder, 10) + 1;
|
||||
|
||||
await fetchAndDisplayOptions();
|
||||
|
||||
} else {
|
||||
const errorText = await response.text();
|
||||
messageDiv.textContent = '선택지 생성 실패: ' + (errorText || '알 수 없는 오류');
|
||||
messageDiv.classList.add('error');
|
||||
messageDiv.style.display = 'block';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('네트워크 오류:', error);
|
||||
messageDiv.textContent = '네트워크 오류가 발생했습니다. 다시 시도해주세요.';
|
||||
messageDiv.classList.add('error');
|
||||
messageDiv.style.display = 'block';
|
||||
}
|
||||
});
|
||||
|
||||
// --- Initial Load Logic ---
|
||||
if (!questionId || questionId === 'null' || questionId === '') {
|
||||
initialMessageDiv.textContent = '경고: 문항 ID가 없습니다. 선택지를 생성할 문항을 먼저 선택해주세요.';
|
||||
initialMessageDiv.classList.add('error');
|
||||
initialMessageDiv.style.display = 'block';
|
||||
existingOptionsListDiv.innerHTML = '<p class="no-options">문항 ID가 없어 선택지 목록을 불러올 수 없습니다.</p>';
|
||||
} else {
|
||||
fetchAndDisplayOptions();
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,6 +1,3 @@
|
|||
설문 생성을 위한 관리자용 HTML 파일을 수정해 드릴게요. AJAX 요청 URL이 XXX.do 형식에 맞도록 업데이트했습니다.
|
||||
|
||||
createSurvey.jsp 수정 내용
|
||||
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
|
||||
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>
|
||||
|
|
@ -114,11 +111,16 @@ createSurvey.jsp 수정 내용
|
|||
<form id="createSurveyForm">
|
||||
<div class="form-group">
|
||||
<label for="surveyTitle">설문 제목:</label>
|
||||
<input type="text" id="surveyTitle" name="surveyTitle" placeholder="예: 2025년 시스템 만족도 설문" required>
|
||||
<input type="text" id="surveyTitle" name="surveyTitle" placeholder="예: 2025년 시스템 만족도 설문" value="2025년 시스템 사용자 경험 및 만족도 평가 설문" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="surveyDescription">설문 설명:</label>
|
||||
<textarea id="surveyDescription" name="surveyDescription" placeholder="설문에 대한 자세한 설명을 입력하세요."></textarea>
|
||||
<textarea id="surveyDescription" name="surveyDescription" placeholder="설문에 대한 자세한 설명을 입력하세요.">안녕하십니까?
|
||||
저희는 사용자 여러분의 소중한 의견을 경청하고, 현재 사용하고 계신 시스템의 만족도를 면밀히 파악하여 더 나은 서비스와 환경을 제공하고자 본 설문조사를 실시하게 되었습니다.
|
||||
본 설문은 2025년 한 해 동안 시스템을 이용하면서 경험하셨던 전반적인 만족도, 사용 편의성, 기능 유용성, 안정성 등에 대한 사용자님의 솔직한 평가를 듣기 위함입니다. 여러분의 피드백은 시스템 개선의 중요한 밑거름이 될 것이며, 향후 시스템 개발 및 운영 방향 설정에 큰 도움이 될 것입니다.
|
||||
설문에 참여해주신 모든 분들께 진심으로 감사드립니다. 설문 응답은 통계적인 목적으로만 활용되며, 개인 정보는 철저히 보호됩니다.
|
||||
참여 대상: 2025년 시스템 사용자 전체소요 시간: 약 5~10분
|
||||
여러분의 적극적인 참여를 부탁드립니다.</textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="startDate">시작일시:</label>
|
||||
|
|
@ -151,13 +153,11 @@ createSurvey.jsp 수정 내용
|
|||
endDate: document.getElementById('endDate').value ? document.getElementById('endDate').value + ':00' : null // 초까지 포함하도록 포맷팅
|
||||
};
|
||||
|
||||
// eGovFrame 환경에서 컨텍스트 경로를 동적으로 가져오는 방법 (JSP 파일일 경우)
|
||||
// HTML 파일에서는 직접 경로를 지정하거나, 웹 서버 설정에 따라 /api/surveys로 직접 접근 가능
|
||||
// 컨텍스트 경로를 동적으로 가져오는 방법 (JSP 파일일 경우)
|
||||
const contextPath = "<%= request.getContextPath() %>"; // JSP일 경우에만 작동
|
||||
// SurveyController의 @RequestMapping("/api/surveys")와 @PostMapping("/createSurvey.do")를 결합
|
||||
const apiUrl = (typeof contextPath !== 'undefined' && contextPath !== null && contextPath !== '') ? contextPath + '/api/surveys/createSurvey.do' : '/api/surveys/createSurvey.do';
|
||||
|
||||
|
||||
try {
|
||||
const response = await fetch(apiUrl, {
|
||||
method: 'POST',
|
||||
|
|
@ -173,6 +173,14 @@ createSurvey.jsp 수정 내용
|
|||
messageDiv.classList.add('success');
|
||||
messageDiv.style.display = 'block';
|
||||
this.reset(); // 폼 필드 초기화
|
||||
|
||||
// 설문 생성 성공 후 createQuestion.jsp로 이동
|
||||
// AdminSurveyPageController의 /admin/survey/{surveyId}/createQuestionForm.do 경로로 이동
|
||||
const redirectUrl = (typeof contextPath !== 'undefined' && contextPath !== null && contextPath !== '') ?
|
||||
contextPath + '/admin/survey/' + result.surveyId + '/createQuestionForm.do' :
|
||||
'/admin/survey/' + result.surveyId + '/createQuestionForm.do';
|
||||
window.location.href = redirectUrl; // 페이지 리다이렉트
|
||||
|
||||
} else { // HTTP 상태 코드가 200-299 범위를 벗어난 경우 (실패)
|
||||
const errorText = await response.text(); // 오류 메시지를 텍스트로 가져옴
|
||||
messageDiv.textContent = '설문 생성 실패: ' + (errorText || '알 수 없는 오류');
|
||||
|
|
|
|||
|
|
@ -20,6 +20,9 @@ function goUserInfo(){
|
|||
function goApiInfo(){
|
||||
location.href = '<c:url value="/sgis/adm/selectApiList.do"/>';
|
||||
}
|
||||
function goSurveySystem(){
|
||||
location.href = '<c:url value="/admin/survey/createSurveyForm.do"/>';
|
||||
}
|
||||
</script>
|
||||
<!-- 웹접근성 본문 포커스 시작 -->
|
||||
<ul id="skipToContent">
|
||||
|
|
@ -90,6 +93,9 @@ function goApiInfo(){
|
|||
<li class="classic-menu-dropdown">
|
||||
<a href="javascript:goApiInfo();">API 관리</a>
|
||||
</li>
|
||||
<li class="classic-menu-dropdown">
|
||||
<a href="javascript:goSurveySystem();">설문 관리</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -141,6 +141,7 @@
|
|||
</if>
|
||||
<![CDATA[
|
||||
ORDER BY A.PROJECT_CODE desc
|
||||
LIMIT 1
|
||||
]]>
|
||||
</select>
|
||||
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@
|
|||
question_order,
|
||||
is_required
|
||||
) VALUES (
|
||||
#{questionId, jdbcType=OTHER},
|
||||
#{surveyId, jdbcType=OTHER},
|
||||
CAST(#{questionId, jdbcType=OTHER} AS uuid), <!-- 이 부분을 수정했습니다. -->
|
||||
CAST(#{surveyId, jdbcType=OTHER} AS uuid), <!-- 이 부분을 수정했습니다. -->
|
||||
#{questionText},
|
||||
#{questionType},
|
||||
#{questionOrder},
|
||||
|
|
@ -30,7 +30,6 @@
|
|||
)
|
||||
</insert>
|
||||
|
||||
<!-- 설문 ID로 문항 목록 조회 -->
|
||||
<select id="findQuestionsBySurveyId" resultMap="questionResultMap">
|
||||
SELECT
|
||||
question_id,
|
||||
|
|
@ -40,11 +39,10 @@
|
|||
question_order,
|
||||
is_required
|
||||
FROM tb_questions
|
||||
WHERE survey_id = #{surveyId, jdbcType=OTHER}
|
||||
WHERE survey_id = CAST(#{surveyId, jdbcType=OTHER} AS uuid)
|
||||
ORDER BY question_order ASC
|
||||
</select>
|
||||
|
||||
<!-- ID로 특정 문항 조회 -->
|
||||
<select id="findQuestionById" resultMap="questionResultMap">
|
||||
SELECT
|
||||
question_id,
|
||||
|
|
@ -54,10 +52,9 @@
|
|||
question_order,
|
||||
is_required
|
||||
FROM tb_questions
|
||||
WHERE question_id = #{questionId, jdbcType=OTHER}
|
||||
WHERE question_id = CAST(#{questionId, jdbcType=OTHER} AS uuid)
|
||||
</select>
|
||||
|
||||
<!-- 문항 업데이트 -->
|
||||
<update id="updateQuestion" parameterType="sgis.surveysystem.domain.Question">
|
||||
UPDATE tb_questions
|
||||
SET
|
||||
|
|
@ -65,12 +62,11 @@
|
|||
question_type = #{questionType},
|
||||
question_order = #{questionOrder},
|
||||
is_required = #{isRequired}
|
||||
WHERE question_id = #{questionId, jdbcType=OTHER}
|
||||
WHERE question_id = CAST(#{questionId, jdbcType=OTHER} AS uuid)
|
||||
</update>
|
||||
|
||||
<!-- 문항 삭제 -->
|
||||
<delete id="deleteQuestion">
|
||||
DELETE FROM tb_questions
|
||||
WHERE question_id = #{questionId, jdbcType=OTHER}
|
||||
WHERE question_id = CAST(#{questionId, jdbcType=OTHER} AS uuid)
|
||||
</delete>
|
||||
</mapper>
|
||||
|
|
@ -9,6 +9,7 @@
|
|||
<result property="optionOrder" column="option_order"/>
|
||||
</resultMap>
|
||||
|
||||
<!-- 선택지 삽입 -->
|
||||
<insert id="insertQuestionOption" parameterType="sgis.surveysystem.domain.QuestionOption">
|
||||
INSERT INTO tb_question_options (
|
||||
option_id,
|
||||
|
|
@ -16,8 +17,8 @@
|
|||
option_text,
|
||||
option_order
|
||||
) VALUES (
|
||||
#{optionId, jdbcType=OTHER},
|
||||
#{questionId, jdbcType=OTHER},
|
||||
CAST(#{optionId, jdbcType=OTHER} AS uuid), <!-- 이 부분을 수정했습니다. -->
|
||||
CAST(#{questionId, jdbcType=OTHER} AS uuid), <!-- 이 부분을 수정했습니다. -->
|
||||
#{optionText},
|
||||
#{optionOrder}
|
||||
)
|
||||
|
|
@ -30,7 +31,7 @@
|
|||
option_text,
|
||||
option_order
|
||||
FROM tb_question_options
|
||||
WHERE question_id = #{questionId, jdbcType=OTHER}
|
||||
WHERE question_id = CAST(#{questionId, jdbcType=OTHER} AS uuid)
|
||||
ORDER BY option_order ASC
|
||||
</select>
|
||||
|
||||
|
|
@ -41,7 +42,7 @@
|
|||
option_text,
|
||||
option_order
|
||||
FROM tb_question_options
|
||||
WHERE option_id = #{optionId, jdbcType=OTHER}
|
||||
WHERE option_id = CAST(#{optionId, jdbcType=OTHER} AS uuid)
|
||||
</select>
|
||||
|
||||
<update id="updateQuestionOption" parameterType="sgis.surveysystem.domain.QuestionOption">
|
||||
|
|
@ -49,11 +50,11 @@
|
|||
SET
|
||||
option_text = #{optionText},
|
||||
option_order = #{optionOrder}
|
||||
WHERE option_id = #{optionId, jdbcType=OTHER}
|
||||
WHERE option_id = CAST(#{optionId, jdbcType=OTHER} AS uuid)
|
||||
</update>
|
||||
|
||||
<delete id="deleteQuestionOption">
|
||||
DELETE FROM tb_question_options
|
||||
WHERE option_id = #{optionId, jdbcType=OTHER}
|
||||
WHERE option_id = CAST(#{optionId, jdbcType=OTHER} AS uuid)
|
||||
</delete>
|
||||
</mapper>
|
||||
</mapper>
|
||||
|
|
|
|||
|
|
@ -24,7 +24,8 @@
|
|||
start_date,
|
||||
end_date
|
||||
) VALUES (
|
||||
#{surveyId, jdbcType=OTHER}, #{surveyTitle},
|
||||
CAST(#{surveyId, jdbcType=OTHER} AS uuid),
|
||||
#{surveyTitle},
|
||||
#{surveyDescription},
|
||||
#{createdAt},
|
||||
#{updatedAt},
|
||||
|
|
@ -58,7 +59,7 @@
|
|||
start_date,
|
||||
end_date
|
||||
FROM tb_surveys
|
||||
WHERE survey_id = #{surveyId, jdbcType=OTHER}
|
||||
WHERE survey_id = CAST(#{surveyId, jdbcType=OTHER} AS uuid) <!-- 이 부분을 수정했습니다. -->
|
||||
</select>
|
||||
|
||||
<update id="updateSurvey" parameterType="sgis.surveysystem.domain.Survey">
|
||||
|
|
@ -70,7 +71,7 @@
|
|||
is_active = #{isActive},
|
||||
start_date = #{startDate},
|
||||
end_date = #{endDate}
|
||||
WHERE survey_id = #{surveyId, jdbcType=OTHER}
|
||||
WHERE survey_id = CAST(#{surveyId, jdbcType=OTHER} AS uuid) <!-- 이 부분도 일관성을 위해 수정합니다. -->
|
||||
</update>
|
||||
|
||||
<update id="updateSurveyStatus">
|
||||
|
|
@ -78,11 +79,11 @@
|
|||
SET
|
||||
is_active = #{isActive},
|
||||
updated_at = NOW()
|
||||
WHERE survey_id = #{surveyId, jdbcType=OTHER}
|
||||
WHERE survey_id = CAST(#{surveyId, jdbcType=OTHER} AS uuid) <!-- 이 부분도 일관성을 위해 수정합니다. -->
|
||||
</update>
|
||||
|
||||
<delete id="deleteSurvey">
|
||||
DELETE FROM tb_surveys
|
||||
WHERE survey_id = #{surveyId, jdbcType=OTHER}
|
||||
WHERE survey_id = CAST(#{surveyId, jdbcType=OTHER} AS uuid) <!-- 이 부분도 일관성을 위해 수정합니다. -->
|
||||
</delete>
|
||||
</mapper>
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -1,5 +1,5 @@
|
|||
#Generated by Maven Integration for Eclipse
|
||||
#Sun Jul 06 01:28:23 KST 2025
|
||||
#Fri Jul 11 19:00:12 KST 2025
|
||||
version=1.0.0
|
||||
groupId=smart_ground
|
||||
m2e.projectName=sgis
|
||||
|
|
|
|||
|
|
@ -25,36 +25,26 @@
|
|||
</properties>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>mvn2</id>
|
||||
<url>http://repo1.maven.org/maven2/</url>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>egovframe</id>
|
||||
<url>http://www.egovframe.go.kr/maven/</url>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>egovframe2</id>
|
||||
<url>http://maven.egovframe.kr:8080/maven/</url>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>mvn2s</id>
|
||||
<url>https://repo1.maven.org/maven2/</url>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>egovframe</id>
|
||||
<url>https://maven.egovframe.go.kr/maven/</url>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>osgeo</id>
|
||||
<name>OSGeo Release Repository</name>
|
||||
|
|
@ -299,22 +289,22 @@
|
|||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
<version>2.12.5</version>
|
||||
<version>2.12.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>2.12.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||
<version>2.12.5</version>
|
||||
<version>2.12.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
<version>2.12.5</version>
|
||||
<version>2.12.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||
<version>2.12.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.googlecode.json-simple</groupId>
|
||||
|
|
@ -480,7 +470,7 @@
|
|||
<build>
|
||||
<defaultGoal>install</defaultGoal>
|
||||
<directory>${basedir}/target</directory>
|
||||
<finalName>sht_webapp</finalName>
|
||||
<finalName>smart_ground</finalName>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
|
|
|
|||
Loading…
Reference in New Issue