Compare commits
10 Commits
2c9d79f447
...
4a2d51a2dd
| Author | SHA1 | Date |
|---|---|---|
|
|
4a2d51a2dd | |
|
|
11930b8b47 | |
|
|
48c0630355 | |
|
|
4fb99fdb08 | |
|
|
026db9d758 | |
|
|
fbb64ffa5b | |
|
|
f68637b6fc | |
|
|
83a0fbdad9 | |
|
|
f0f09432c3 | |
|
|
94fc429640 |
|
|
@ -25,7 +25,6 @@
|
|||
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
<attribute name="org.eclipse.jst.component.dependency" value="/WEB-INF/lib"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.jst.server.core.container/org.eclipse.jst.server.tomcat.runtimeTarget/Apache Tomcat v8.5 (2)">
|
||||
|
|
@ -33,9 +32,9 @@
|
|||
<attribute name="owner.project.facets" value="jst.web"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/jdk1.8.0_251">
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
|
||||
<attributes>
|
||||
<attribute name="owner.project.facets" value="java"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="output" path="target/classes"/>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
16
list.txt
16
list.txt
|
|
@ -1,8 +1,10 @@
|
|||
src\main\webapp\WEB-INF\jsp\sgis\com\common\header.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
|
||||
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\WEB-INF\jsp\sgis\portal\portalMain.jsp
|
||||
src\main\webapp\com\img\common\main\temp_dummy_main.png
|
||||
63
pom.xml
63
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,17 +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>
|
||||
<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>
|
||||
|
|
@ -475,7 +470,7 @@
|
|||
<build>
|
||||
<defaultGoal>install</defaultGoal>
|
||||
<directory>${basedir}/target</directory>
|
||||
<finalName>sht_webapp</finalName>
|
||||
<finalName>smart_ground</finalName>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
package sgis.attach.entity;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class Attach {
|
||||
|
||||
private long attachment_id; //파일 PK
|
||||
private long postId; //게시글아이디
|
||||
private String fileName; //원본 파일명
|
||||
private String storedFileName; //서버에 저장된 고유 파일명 (UUID등)
|
||||
private String filePath; //파일 저장 경로
|
||||
private long fileSize; //파일 크기(바이트)
|
||||
private String fileType; //파일 MIME(타입(e.g., image/jpeg)
|
||||
private long downloadCount; //다운로드횟수
|
||||
private String createdAt; //생성일
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package sgis.attach.mapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import sgis.attach.entity.Attach;
|
||||
|
||||
@Mapper //- Mybatis API
|
||||
public interface AttachMapper {
|
||||
public List<Attach> selectAttachList();
|
||||
|
||||
public int insertAttachList(List<Attach> attachList);
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package sgis.attach.service;
|
||||
|
||||
import java.util.List;
|
||||
import sgis.attach.entity.Attach;
|
||||
|
||||
public interface AttachService {
|
||||
|
||||
/**
|
||||
* 모든 첨부파일을 조회합니다.
|
||||
* @return 모든 Attach 객체의 리스트
|
||||
*/
|
||||
List<Attach> getLists();
|
||||
|
||||
/**
|
||||
* 새 게시글을 생성합니다.
|
||||
* @param post 생성할 Post 객체
|
||||
* @return 생성된 Post 객체 (ID 포함)
|
||||
*/
|
||||
boolean insertAttachList(List<Attach> attachList);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
package sgis.attach.service.impl;
|
||||
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import sgis.attach.entity.Attach;
|
||||
import sgis.attach.mapper.AttachMapper;
|
||||
import sgis.attach.service.AttachService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class AttachServiceImpl implements AttachService {
|
||||
|
||||
private final AttachMapper attachMapper;
|
||||
|
||||
@Autowired
|
||||
public AttachServiceImpl(AttachMapper attachMapper) {
|
||||
this.attachMapper = attachMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Attach> getLists() {
|
||||
return attachMapper.selectAttachList();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public boolean insertAttachList(List<Attach> attachList) {
|
||||
int insertedRows = attachMapper.insertAttachList(attachList);
|
||||
if (insertedRows > 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -74,7 +74,7 @@ public class BoardController{
|
|||
if( nBoardCategoryId != null ) {
|
||||
switch (nBoardCategoryId) {
|
||||
case 1:
|
||||
displayName = "커뮤니티";
|
||||
displayName = "자료실";
|
||||
break;
|
||||
case 2:
|
||||
displayName = "공지사항";
|
||||
|
|
@ -127,7 +127,7 @@ public class BoardController{
|
|||
@RequestMapping("/sgis/portal/board-community.do")
|
||||
public String boardCommunity(@RequestParam HashMap<String,Object> params, ModelMap model, RedirectAttributes rttr, HttpSession session) {
|
||||
model.put("boardCategoryId", 1);
|
||||
model.put("displayName", "커뮤니티");
|
||||
model.put("displayName", "자료실");
|
||||
return boardList(params, model, rttr, session);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
package sgis.board.controller;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.OffsetDateTime; // OffsetDateTime import 추가
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
|
@ -15,7 +17,6 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute; // ModelAttribute import는 다른 메서드를 위해 유지
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
|
|
@ -24,20 +25,23 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
|
||||
import egovframework.com.cmm.util.EgovUserDetailsHelper;
|
||||
import sgis.attach.entity.Attach;
|
||||
import sgis.attach.service.AttachService;
|
||||
import sgis.board.entity.Board;
|
||||
import sgis.board.entity.Member;
|
||||
import sgis.board.entity.Post; // Post entity import
|
||||
import sgis.board.mapper.BoardMapper;
|
||||
import sgis.board.mapper.MemberMapper;
|
||||
import sgis.board.mapper.PostMapper;
|
||||
import sgis.board.service.PostService;
|
||||
import sgis.com.util.FileUploadUtil;
|
||||
import sgis.com.vo.FileVO;
|
||||
import sgis.com.vo.SessionVO;
|
||||
import sgis.com.web.BaseController;
|
||||
|
||||
|
|
@ -50,6 +54,9 @@ public class BoardRestController extends BaseController {
|
|||
@Autowired
|
||||
PostService postService;
|
||||
|
||||
@Autowired
|
||||
AttachService attachService;
|
||||
|
||||
@Autowired
|
||||
MemberMapper memberMapper;
|
||||
|
||||
|
|
@ -192,7 +199,8 @@ public class BoardRestController extends BaseController {
|
|||
@RequestParam("boardContent") String content,
|
||||
// Using @RequestParam(value = "parentPostId", required = false) for optionality.
|
||||
// If it comes as an empty string, it will be null.
|
||||
@RequestParam(value = "parentPostId", required = false) Long parentPostId // This is good
|
||||
@RequestParam(value = "parentPostId", required = false) Long parentPostId/*, // This is good
|
||||
@RequestParam("files") List<MultipartFile> files*/
|
||||
) {
|
||||
Post newPost = new Post();
|
||||
newPost.setBoardCategoryId(boardCategoryId);
|
||||
|
|
@ -233,6 +241,41 @@ public class BoardRestController extends BaseController {
|
|||
// PostService를 통해 게시글 생성
|
||||
Post createdPost = postService.createPost(newPost);
|
||||
|
||||
// 게시글 등록 성공시 파일 서버 저장 및 테이블 insert 처리
|
||||
/* Date nowDate = new Date();
|
||||
String strNowYyyy = new SimpleDateFormat("yyyy").format(nowDate);
|
||||
String strNowMm = new SimpleDateFormat("MM").format(nowDate);
|
||||
String fileUploadPath = "uploads/files/" + strNowYyyy + "/" + strNowMm + "/";
|
||||
HashMap<String, Object> uploadedFileList = FileUploadUtil.multiUploadFormFile(files, fileUploadPath);
|
||||
List<Attach> tbFileList = new ArrayList<Attach>(); // TB_ATTACHMENT에 insert할 리스트데이타
|
||||
// 서버에 파일 업로드 처리 후, 성공하거나 util이 처리한 파일 데이타를 리턴하면 아래 수행
|
||||
if (uploadedFileList != null && uploadedFileList.containsKey("file_list")) {
|
||||
Object fileListObj = uploadedFileList.get("file_list");
|
||||
|
||||
if (fileListObj instanceof List<?>) {
|
||||
List<?> fileList = (List<?>) fileListObj;
|
||||
|
||||
for (Object obj : fileList) {
|
||||
if (obj instanceof FileVO) {
|
||||
FileVO fileVO = (FileVO) obj;
|
||||
Attach attach = new Attach();
|
||||
attach.setPostId(createdPost.getPostId());
|
||||
attach.setFileName(fileVO.getFileName()); // 원본파일명
|
||||
attach.setStoredFileName(fileVO.getStoredFileName()); // 실제저장파일명
|
||||
attach.setFilePath(fileVO.getFilePath()); // 파일저장경로
|
||||
attach.setFileSize(fileVO.getFileSize()); // 파일크기
|
||||
attach.setFileType(fileVO.getFileType()); // 파일MIME타입
|
||||
|
||||
tbFileList.add(attach);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tbFileList.size()>0) {
|
||||
attachService.insertAttachList(tbFileList);
|
||||
}
|
||||
}*/
|
||||
|
||||
// 생성된 Post 객체 반환 (클라이언트에게 성공 여부 및 생성된 게시글 정보 전달)
|
||||
return createdPost;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
package sgis.board.typehandler;
|
||||
|
||||
import org.apache.ibatis.type.BaseTypeHandler;
|
||||
import org.apache.ibatis.type.JdbcType;
|
||||
import org.apache.ibatis.type.MappedJdbcTypes;
|
||||
import org.apache.ibatis.type.MappedTypes;
|
||||
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.UUID;
|
||||
|
||||
@MappedTypes(UUID.class)
|
||||
@MappedJdbcTypes(JdbcType.OTHER) // Or JdbcType.VARCHAR if storing as string in DB
|
||||
public class UUIDTypeHandler extends BaseTypeHandler<UUID> {
|
||||
|
||||
@Override
|
||||
public void setNonNullParameter(PreparedStatement ps, int i, UUID parameter, JdbcType jdbcType) throws SQLException {
|
||||
ps.setString(i, parameter.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getNullableResult(ResultSet rs, String columnName) throws SQLException {
|
||||
String uuidString = rs.getString(columnName);
|
||||
return uuidString == null ? null : UUID.fromString(uuidString);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
|
||||
String uuidString = rs.getString(columnIndex);
|
||||
return uuidString == null ? null : UUID.fromString(uuidString);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
|
||||
String uuidString = cs.getString(columnIndex);
|
||||
return uuidString == null ? null : UUID.fromString(uuidString);
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -0,0 +1,97 @@
|
|||
package sgis.com.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.springframework.web.multipart.MultipartHttpServletRequest;
|
||||
|
||||
import egovframework.com.cmm.service.EgovProperties;
|
||||
import sgis.com.vo.FileUtil;
|
||||
import sgis.com.vo.FileVO;
|
||||
import sgis.com.web.BaseController;
|
||||
/**
|
||||
* @FileName : FileUploadUtil.java
|
||||
* @Date : 2025. 7. 15.
|
||||
* @Creator :
|
||||
* @Discription :
|
||||
*/
|
||||
public class FileUploadUtil extends BaseController {
|
||||
//logger 설정
|
||||
private final static Logger logger = LoggerFactory.getLogger(FileUploadUtil.class);
|
||||
|
||||
/**
|
||||
* 파일 업로드 처리 - 다중
|
||||
* @param request 파일 Multipart
|
||||
* @param path uploads/files/yyyy/mm
|
||||
* @return
|
||||
*/
|
||||
public static HashMap<String,Object> multiUploadFormFile(List<MultipartFile> files, String path) {
|
||||
HashMap<String, Object> result = new HashMap<String, Object>(); // 최종리턴 파일목록(DB INSERT 할 데이타)
|
||||
// HttpSession session = request.getSession();
|
||||
MultipartHttpServletRequest mpRequest = (MultipartHttpServletRequest) files;
|
||||
Iterator<String> fileNameIterator = mpRequest.getFileNames();
|
||||
|
||||
String uploadPath = EgovProperties.getProperty("Globals.File.StoredPath").replaceAll("/", "\\");
|
||||
String uploadDirPath = "";
|
||||
if(path.startsWith(System.getProperty("file.separator"))) {
|
||||
uploadDirPath = path.substring(1).replaceAll("/", "\\");
|
||||
} else {
|
||||
uploadDirPath = path.replaceAll("/", "\\");
|
||||
}
|
||||
uploadDirPath = uploadPath + uploadDirPath;
|
||||
String originalFileName = "";
|
||||
String realFileName = "";
|
||||
List<FileVO> fileList = new ArrayList<FileVO>();
|
||||
while (fileNameIterator.hasNext()) {
|
||||
//파일정보를 하나씩 취득한다
|
||||
MultipartFile multiFile = mpRequest.getFile((String) fileNameIterator.next());
|
||||
if (multiFile.getSize() > 0) {
|
||||
originalFileName = multiFile.getOriginalFilename();
|
||||
realFileName = FileUtil.getUniqueFileName(uploadDirPath, originalFileName);
|
||||
|
||||
|
||||
File file = new File(uploadDirPath + realFileName);
|
||||
|
||||
// 디렉토리가 존재 하지 않을 경우 생성
|
||||
if(!file.exists()) {
|
||||
file.setExecutable(false, true);
|
||||
file.setReadable(true);
|
||||
file.setWritable(false, true);
|
||||
file.mkdirs();
|
||||
}
|
||||
try{
|
||||
multiFile.transferTo(file);
|
||||
FileVO uploadFile = new FileVO();
|
||||
// 파일 업로드 파일 원본파일명
|
||||
uploadFile.setFileName(originalFileName);
|
||||
// 파일 업로드 파일 저장파일명
|
||||
uploadFile.setStoredFileName(realFileName);
|
||||
// 파일 업로드 파일 저장경로
|
||||
uploadFile.setFilePath(uploadDirPath);
|
||||
// 파일 업로드 파일 크기
|
||||
uploadFile.setFileSize(multiFile.getSize());
|
||||
// 파일 업로드 파일 MIME타입
|
||||
uploadFile.setFileType(multiFile.getContentType());
|
||||
|
||||
|
||||
fileList.add(uploadFile);
|
||||
} catch (IllegalStateException e) {
|
||||
logger.error("IllegalStateException");
|
||||
// 에러메세지리턴
|
||||
} catch (IOException e) {
|
||||
logger.error("IOException");
|
||||
// 에러메세지리턴
|
||||
}
|
||||
}
|
||||
}
|
||||
result.put("file_list", fileList);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,6 @@ import java.text.DecimalFormat;
|
|||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* @FileName : StringUtil.java
|
||||
|
|
|
|||
|
|
@ -0,0 +1,63 @@
|
|||
package sgis.com.vo;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.UUID;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* 파일 처리 구현 클래스
|
||||
* @author
|
||||
*/
|
||||
public class FileUtil {
|
||||
/** 로그처리 객체 */
|
||||
private static final Logger logger = LoggerFactory.getLogger(FileUtil.class);
|
||||
|
||||
|
||||
/**
|
||||
* 파일 이름이 디렉토리에 이미 존재하는지 체크한다
|
||||
* @param realPath 업로드디렉토리
|
||||
* @param fileName 업로드파일이름
|
||||
* @return String 파일이름
|
||||
*/
|
||||
public static String getUniqueFileName(String realPath, String fileName) {
|
||||
|
||||
int idx = 0;
|
||||
int cnt = 1;
|
||||
String temp = getSavedFileName(fileName); // 저장할 파일명 생성
|
||||
File tempFile = new File(realPath+temp); // 기존에 동일한 파일명의 파일 존재하는지 체크
|
||||
|
||||
//이미존재하는 파일인지 확인
|
||||
while(tempFile.exists()) {
|
||||
//기존에 존재하면 확장자랑 파일명을 분리해서 [숫자]를 붙여서 파일이름을 짓는다
|
||||
if((idx=fileName.indexOf(".")) != -1){
|
||||
String left = fileName.substring(0, idx);
|
||||
String right = fileName.substring(idx);
|
||||
temp = left + "[" + cnt + "]" + right;
|
||||
}else{
|
||||
temp = fileName + "[" + cnt + "]";
|
||||
}
|
||||
|
||||
tempFile = new File(realPath+temp);
|
||||
cnt++;
|
||||
}
|
||||
//실제 저장될 파일이름 저장
|
||||
fileName = temp;
|
||||
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public static String getSavedFileName(String originalFileName) {
|
||||
// 확장자 추출
|
||||
String ext = "";
|
||||
int lastDot = originalFileName.lastIndexOf('.');
|
||||
if (lastDot != -1) {
|
||||
ext = originalFileName.substring(lastDot); // .pdf, .jpg 등
|
||||
}
|
||||
|
||||
// UUID 생성 + 확장자 붙이기
|
||||
String savedFileName = UUID.randomUUID().toString() + ext;
|
||||
return savedFileName;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
package sgis.com.vo;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* @FileName : FileVO.java
|
||||
* @Date : 2025. 7. 15.
|
||||
* @Creator :
|
||||
* @Discription : file vo
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class FileVO implements Serializable {
|
||||
//serialVersionUID
|
||||
private static final long serialVersionUID = 6268293686980181848L;
|
||||
private long attachment_id; //파일 PK
|
||||
private String postId; //게시글아이디
|
||||
private String fileName; //원본 파일명
|
||||
private String storedFileName; //서버에 저장된 고유 파일명 (UUID등)
|
||||
private String filePath; //파일 저장 경로
|
||||
private long fileSize; //파일 크기(바이트)
|
||||
private String fileType; //파일 MIME(타입(e.g., image/jpeg)
|
||||
private long downloadCount; //다운로드횟수
|
||||
private String createdAt; //생성일
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -2,13 +2,53 @@ package sgis.map.mapper;
|
|||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import egovframework.rte.psl.dataaccess.mapper.Mapper;
|
||||
import egovframework.rte.psl.dataaccess.util.EgovMap;
|
||||
|
||||
@Mapper("MapMainMapper")
|
||||
@Mapper("mapMainMapper")
|
||||
public interface MapMainMapper {
|
||||
|
||||
EgovMap selectMapInfoList(HashMap<String, Object> params) throws Exception;
|
||||
|
||||
EgovMap selectHoleCount(Map<String, Object> params) throws Exception;
|
||||
|
||||
List<EgovMap> selectHoleList(Map<String, Object> params) throws Exception;
|
||||
|
||||
List<EgovMap> selectItems(Map<String, Object> params) throws Exception;
|
||||
|
||||
List<EgovMap> selectDistrict() throws Exception;
|
||||
|
||||
List<EgovMap> selectGrid1(Map<String, Object> params) throws Exception;
|
||||
|
||||
List<EgovMap> selectDistrictSgg(Map<String, Object> params) throws Exception;
|
||||
|
||||
List<EgovMap> selectOldItems(Map<String, Object> params) throws Exception;
|
||||
|
||||
List<EgovMap> get3dSido(Map<String, Object> params) throws Exception;
|
||||
|
||||
List<EgovMap> get3dGugun(Map<String, Object> params) throws Exception;
|
||||
|
||||
List<EgovMap> get3dDong(Map<String, Object> params) throws Exception;
|
||||
|
||||
List<EgovMap> get3dDetail(Map<String, Object> params) throws Exception;
|
||||
|
||||
EgovMap getGeneralData(Map<String, Object> params) throws Exception;
|
||||
|
||||
List<EgovMap> getLinkLayerData(Map<String, Object> params) throws Exception;
|
||||
|
||||
List<EgovMap> getSPTData(Map<String, Object> params) throws Exception;
|
||||
|
||||
List<EgovMap> getSampleData(Map<String, Object> params) throws Exception;
|
||||
|
||||
EgovMap selectInfoLastPage(Map<String, Object> params) throws Exception;
|
||||
|
||||
List<EgovMap> selectInfoItems(Map<String, Object> params) throws Exception;
|
||||
|
||||
EgovMap selectWebDownloadLog(Map<String, Object> params) throws Exception; // dhlee 사용자별 다운로드 요청한 개수를 얻어온다.
|
||||
|
||||
List<EgovMap> selectProjectList(Map<String, Object> params) throws Exception;
|
||||
|
||||
List<EgovMap> mapBoreholeLogHeader(Map<String, Object> params) throws Exception;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package sgis.map.service;
|
|||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import egovframework.rte.psl.dataaccess.util.EgovMap;
|
||||
import sgis.map.vo.MapInfoVO;
|
||||
|
|
@ -10,4 +11,44 @@ public interface MapMainService {
|
|||
|
||||
EgovMap selectMapInfoList(HashMap<String, Object> params) throws Exception;
|
||||
|
||||
|
||||
EgovMap selectHoleCount(Map<String, Object> params) throws Exception;
|
||||
|
||||
List<EgovMap> selectHoleList(Map<String, Object> params) throws Exception;
|
||||
|
||||
List<EgovMap> selectItems(Map<String, Object> params) throws Exception;
|
||||
|
||||
List<EgovMap> selectDistrict() throws Exception;
|
||||
|
||||
List<EgovMap> selectGrid1(Map<String, Object> params) throws Exception;
|
||||
|
||||
List<EgovMap> selectDistrictSgg(Map<String, Object> params) throws Exception;
|
||||
|
||||
List<EgovMap> selectOldItems(Map<String, Object> params) throws Exception;
|
||||
|
||||
List<EgovMap> get3dSido(Map<String, Object> params) throws Exception;
|
||||
|
||||
List<EgovMap> get3dGugun(Map<String, Object> params) throws Exception;
|
||||
|
||||
List<EgovMap> get3dDong(Map<String, Object> params) throws Exception;
|
||||
|
||||
List<EgovMap> get3dDetail(Map<String, Object> params) throws Exception;
|
||||
|
||||
EgovMap getGeneralData(Map<String, Object> params) throws Exception;
|
||||
|
||||
List<EgovMap> getLinkLayerData(Map<String, Object> params) throws Exception;
|
||||
|
||||
List<EgovMap> getSPTData(Map<String, Object> params) throws Exception;
|
||||
|
||||
List<EgovMap> getSampleData(Map<String, Object> params) throws Exception;
|
||||
|
||||
EgovMap selectInfoLastPage(Map<String, Object> params) throws Exception;
|
||||
|
||||
List<EgovMap> selectInfoItems(Map<String, Object> params) throws Exception;
|
||||
|
||||
EgovMap selectWebDownloadLog(Map<String, Object> params) throws Exception; // dhlee 사용자별 다운로드 요청한 개수를 얻어온다.
|
||||
|
||||
List<EgovMap> selectProjectList(Map<String, Object> params) throws Exception; // dhlee
|
||||
|
||||
List<EgovMap> mapBoreholeLogList(Map<String, Object> params) throws Exception;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package sgis.map.service.impl;
|
|||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
|
@ -14,12 +15,117 @@ import sgis.map.service.MapMainService;
|
|||
public class MapMainServiceImpl implements MapMainService {
|
||||
|
||||
@Autowired
|
||||
private MapMainMapper MapMainMapper;
|
||||
private MapMainMapper mapMainMapper;
|
||||
|
||||
@Override
|
||||
public EgovMap selectMapInfoList(HashMap<String, Object> params) throws Exception {
|
||||
// TODO Auto-generated method stub
|
||||
return MapMainMapper.selectMapInfoList(params);
|
||||
return mapMainMapper.selectMapInfoList(params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EgovMap selectHoleCount(Map<String, Object> params) throws Exception {
|
||||
return mapMainMapper.selectHoleCount(params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EgovMap> selectHoleList(Map<String, Object> params) throws Exception {
|
||||
return mapMainMapper.selectHoleList(params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EgovMap> selectItems(Map<String, Object> params) throws Exception {
|
||||
return mapMainMapper.selectItems(params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EgovMap> selectDistrict() throws Exception {
|
||||
return mapMainMapper.selectDistrict();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EgovMap> selectGrid1(Map<String, Object> params) throws Exception {
|
||||
return mapMainMapper.selectGrid1(params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EgovMap> selectDistrictSgg(Map<String, Object> params) throws Exception {
|
||||
return mapMainMapper.selectDistrictSgg(params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EgovMap> selectOldItems(Map<String, Object> params) throws Exception {
|
||||
return mapMainMapper.selectOldItems(params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EgovMap> get3dSido(Map<String, Object> params) throws Exception {
|
||||
return mapMainMapper.get3dSido(params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EgovMap> get3dGugun(Map<String, Object> params) throws Exception {
|
||||
return mapMainMapper.get3dGugun(params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EgovMap> get3dDong(Map<String, Object> params) throws Exception {
|
||||
return mapMainMapper.get3dDong(params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EgovMap> get3dDetail(Map<String, Object> params) throws Exception {
|
||||
return mapMainMapper.get3dDetail(params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EgovMap getGeneralData(Map<String, Object> params) throws Exception {
|
||||
return mapMainMapper.getGeneralData(params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EgovMap> getLinkLayerData(Map<String, Object> params) throws Exception {
|
||||
return mapMainMapper.getLinkLayerData(params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EgovMap> getSPTData(Map<String, Object> params) throws Exception {
|
||||
return mapMainMapper.getSPTData(params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EgovMap> getSampleData(Map<String, Object> params) throws Exception {
|
||||
return mapMainMapper.getSampleData(params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EgovMap selectInfoLastPage(Map<String, Object> params) throws Exception {
|
||||
return mapMainMapper.selectInfoLastPage(params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EgovMap> selectInfoItems(Map<String, Object> params) throws Exception {
|
||||
return mapMainMapper.selectInfoItems(params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EgovMap selectWebDownloadLog(Map<String, Object> params) throws Exception {
|
||||
return mapMainMapper.selectWebDownloadLog(params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EgovMap> selectProjectList(Map<String, Object> params) throws Exception {
|
||||
return mapMainMapper.selectProjectList(params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EgovMap> mapBoreholeLogList(Map<String, Object> params) throws Exception {
|
||||
// TODO Auto-generated method stub
|
||||
//String businessCode = String.valueOf(params.get("businessCode"));
|
||||
//String holeCode = String.valueOf(params.get("holeCode"));
|
||||
|
||||
List<EgovMap> mapBoreholeLogHeader = mapMainMapper.mapBoreholeLogHeader(params);
|
||||
|
||||
return mapBoreholeLogHeader;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,18 +5,21 @@ import java.io.File;
|
|||
import java.io.InputStreamReader;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.ResponseBody; // Add this import
|
||||
|
||||
import egovframework.rte.psl.dataaccess.util.EgovMap;
|
||||
import sgis.app.service.AppInstrumentService;
|
||||
|
|
@ -28,6 +31,7 @@ import sgis.map.service.AppPredictService;
|
|||
import sgis.map.service.MapMainService;
|
||||
import sgis.map.vo.MapInfoVO;
|
||||
|
||||
|
||||
/**
|
||||
* @FileName : MapMainController.java
|
||||
* @Date : 2022. 05. 10
|
||||
|
|
@ -38,7 +42,7 @@ import sgis.map.vo.MapInfoVO;
|
|||
public class MapMainController extends BaseController {
|
||||
|
||||
@Resource(name ="MapMainService")
|
||||
private MapMainService MapMainService;
|
||||
private MapMainService mapMainService;
|
||||
|
||||
//입력시스템 서비스
|
||||
@Resource(name ="AppMainService")
|
||||
|
|
@ -363,4 +367,153 @@ public class MapMainController extends BaseController {
|
|||
|
||||
};
|
||||
|
||||
|
||||
@RequestMapping(value = "/map/sichudanNew.do")
|
||||
public String sichudanNew(ModelMap model, HttpServletRequest request, HttpServletResponse response, @RequestParam Map<String, Object> params) throws Exception {
|
||||
String code = String.valueOf(params.get("code"));
|
||||
|
||||
String data = "";
|
||||
try {
|
||||
String[] hCodes = code.split(",");
|
||||
for (int i = 0; i < hCodes.length; i++) {
|
||||
data = data + hCodes[i].trim() + ",";
|
||||
}
|
||||
data = data.substring(0, data.length() - 1);
|
||||
} catch (NumberFormatException e) {
|
||||
//logger.debug("error", e);
|
||||
data = "";
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
//logger.debug("error", e);
|
||||
data = "";
|
||||
} catch (Exception e) {
|
||||
//logger.debug("error", e);
|
||||
data = "";
|
||||
}
|
||||
|
||||
model.put("data", data);
|
||||
return "/sgis/map/mapInformation/sichudanNew";
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/map/getSichudanData.do", method = RequestMethod.GET, produces = { MediaType.APPLICATION_JSON_VALUE })
|
||||
@ResponseBody
|
||||
public JSONObject getSichudanData(ModelMap model, HttpServletRequest request, HttpServletResponse response, @RequestParam Map<String, Object> params) throws Exception {
|
||||
StringBuffer sb = request.getRequestURL();
|
||||
String url = sb.substring(0, sb.lastIndexOf("/"));
|
||||
|
||||
String code = String.valueOf(params.get("code"));
|
||||
|
||||
|
||||
JSONObject responseJson = new JSONObject();
|
||||
responseJson.put("jsonView", getSectionData(code));
|
||||
return responseJson;
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/map/borehole-log.do")
|
||||
public String mapBoreholeLog(ModelMap model, HttpServletRequest request, HttpServletResponse response, @RequestParam Map<String, Object> params) throws Exception {
|
||||
return "/sgis/map/mapInformation/boreholeLog";
|
||||
}
|
||||
|
||||
/**
|
||||
* 지반 주상도 생성에 필요한 정보를 얻는다.
|
||||
* @param model
|
||||
* @param request
|
||||
* @param response
|
||||
* @param params
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
@RequestMapping(value = "/map/borehole-log/list.do", method = RequestMethod.GET, produces = { MediaType.APPLICATION_JSON_VALUE })
|
||||
@ResponseBody
|
||||
public JSONObject mapBoreholeLogList(ModelMap model, HttpServletRequest request, HttpServletResponse response, @RequestParam Map<String, Object> params) throws Exception {
|
||||
StringBuffer sb = request.getRequestURL();
|
||||
String url = sb.substring(0, sb.lastIndexOf("/"));
|
||||
|
||||
JSONObject responseJson = new JSONObject();
|
||||
responseJson.put("list", mapMainService.mapBoreholeLogList(params));
|
||||
return responseJson;
|
||||
}
|
||||
|
||||
public JSONObject getSectionData(String code) throws Exception {
|
||||
|
||||
code = code.trim();
|
||||
|
||||
Map<String, Object> params = new HashMap<String, Object>();
|
||||
|
||||
|
||||
String[] bussinessNHoleCode = code.split("\\|\\|");
|
||||
|
||||
params.put("businessCode", bussinessNHoleCode[0]);
|
||||
params.put("holeCode", bussinessNHoleCode[1]);
|
||||
|
||||
|
||||
EgovMap general = mapMainService.getGeneralData(params);
|
||||
|
||||
if( general == null ) {
|
||||
// 예외처리 추가하기
|
||||
}
|
||||
String[] values = String.valueOf(general.get("vlu")).split("\\|");
|
||||
|
||||
JSONObject resultJson = new JSONObject();
|
||||
resultJson.put("PCODE", values[0]);
|
||||
resultJson.put("ALTITUDE", values[1]);
|
||||
resultJson.put("DEPTH", values[2]);
|
||||
|
||||
JSONObject position = new JSONObject();
|
||||
position.put("X", values[3]);
|
||||
position.put("Y", values[4]);
|
||||
|
||||
resultJson.put("TM", position);
|
||||
|
||||
position = new JSONObject();
|
||||
position.put("X", values[5]);
|
||||
position.put("Y", values[6]);
|
||||
|
||||
resultJson.put("LL", position);
|
||||
|
||||
resultJson.put("PNAME", values[7]);
|
||||
if( 8 < values.length && values[8] != null ) {
|
||||
resultJson.put("WATER", values[8]);
|
||||
} else {
|
||||
resultJson.put("WATER", "");
|
||||
}
|
||||
|
||||
if (values.length == 10) {
|
||||
resultJson.put("PCOM", values[9]);
|
||||
} else {
|
||||
resultJson.put("PCOM", "");
|
||||
}
|
||||
|
||||
JSONObject generalData = resultJson;
|
||||
generalData.put("HCODE", code);
|
||||
|
||||
List<EgovMap> linkLayerData = mapMainService.getLinkLayerData(params);
|
||||
//generalData.put("LAYER", listmap_to_json_string(linkLayerData));
|
||||
generalData.put("LAYER", linkLayerData);
|
||||
|
||||
List<EgovMap> SPTData = mapMainService.getSPTData(params);
|
||||
//generalData.put("SPT", listmap_to_json_string(SPTData));
|
||||
generalData.put("SPT", SPTData);
|
||||
|
||||
List<EgovMap> sampleData = mapMainService.getSampleData(params);
|
||||
//generalData.put("SAMPLE", listmap_to_json_string(sampleData));
|
||||
generalData.put("SAMPLE", sampleData);
|
||||
|
||||
return generalData;
|
||||
|
||||
}
|
||||
|
||||
public String listmap_to_json_string(List<EgovMap> list) {
|
||||
JSONArray json_arr = new JSONArray();
|
||||
for (Map<String, Object> map : list) {
|
||||
JSONObject json_obj = new JSONObject();
|
||||
for (Map.Entry<String, Object> entry : map.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
json_obj.put(key, value);
|
||||
}
|
||||
json_arr.add(json_obj);
|
||||
}
|
||||
return json_arr.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
package sgis.surveysystem.config;
|
||||
|
||||
import org.apache.ibatis.type.BaseTypeHandler;
|
||||
import org.apache.ibatis.type.JdbcType;
|
||||
import org.apache.ibatis.type.MappedJdbcTypes;
|
||||
import org.apache.ibatis.type.MappedTypes;
|
||||
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Timestamp;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* java.time.LocalDateTime 객체와 JDBC Timestamp (PostgreSQL TIMESTAMP WITH TIME ZONE) 컬럼 간의 매핑을 처리하는 MyBatis TypeHandler.
|
||||
*/
|
||||
@MappedJdbcTypes(JdbcType.TIMESTAMP) // 이 TypeHandler가 처리할 JDBC 타입
|
||||
@MappedTypes(LocalDateTime.class) // 이 TypeHandler가 처리할 Java 타입
|
||||
public class LocalDateTimeTypeHandler extends BaseTypeHandler<LocalDateTime> {
|
||||
|
||||
@Override
|
||||
public void setNonNullParameter(PreparedStatement ps, int i, LocalDateTime parameter, JdbcType jdbcType) throws SQLException {
|
||||
// LocalDateTime을 JDBC Timestamp로 변환하여 PreparedStatement에 설정
|
||||
ps.setTimestamp(i, Timestamp.valueOf(parameter));
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalDateTime getNullableResult(ResultSet rs, String columnName) throws SQLException {
|
||||
// ResultSet에서 컬럼 이름으로 Timestamp 값을 가져와 LocalDateTime으로 변환
|
||||
Timestamp ts = rs.getTimestamp(columnName);
|
||||
return (ts != null) ? ts.toLocalDateTime() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalDateTime getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
|
||||
// ResultSet에서 컬럼 인덱스로 Timestamp 값을 가져와 LocalDateTime으로 변환
|
||||
Timestamp ts = rs.getTimestamp(columnIndex);
|
||||
return (ts != null) ? ts.toLocalDateTime() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalDateTime getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
|
||||
// CallableStatement에서 Timestamp 값을 가져와 LocalDateTime으로 변환
|
||||
Timestamp ts = cs.getTimestamp(columnIndex);
|
||||
return (ts != null) ? ts.toLocalDateTime() : null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
package sgis.surveysystem.config;
|
||||
|
||||
import org.apache.ibatis.type.BaseTypeHandler;
|
||||
import org.apache.ibatis.type.JdbcType;
|
||||
import org.apache.ibatis.type.MappedJdbcTypes;
|
||||
import org.apache.ibatis.type.MappedTypes;
|
||||
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* java.util.UUID 객체와 PostgreSQL의 UUID (JDBC Type.OTHER) 컬럼 간의 매핑을 처리하는 MyBatis TypeHandler.
|
||||
*/
|
||||
@MappedJdbcTypes(JdbcType.OTHER) // 이 TypeHandler가 처리할 JDBC 타입 (PostgreSQL의 UUID는 OTHER로 인식됨)
|
||||
@MappedTypes(UUID.class) // 이 TypeHandler가 처리할 Java 타입
|
||||
public class UUIDTypeHandler extends BaseTypeHandler<UUID> {
|
||||
|
||||
@Override
|
||||
public void setNonNullParameter(PreparedStatement ps, int i, UUID parameter, JdbcType jdbcType) throws SQLException {
|
||||
// Java UUID 객체를 JDBC PreparedStatement에 설정할 때
|
||||
// PostgreSQL의 UUID 타입은 String으로 변환하여 설정하거나, setObject(UUID)를 직접 사용할 수 있습니다.
|
||||
// setString이 더 안전하고 호환성이 높습니다.
|
||||
ps.setString(i, parameter.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getNullableResult(ResultSet rs, String columnName) throws SQLException {
|
||||
// ResultSet에서 컬럼 이름으로 값을 가져와 Java UUID 객체로 변환할 때
|
||||
String uuidString = rs.getString(columnName);
|
||||
return (uuidString != null) ? UUID.fromString(uuidString) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
|
||||
// ResultSet에서 컬럼 인덱스로 값을 가져와 Java UUID 객체로 변환할 때
|
||||
String uuidString = rs.getString(columnIndex);
|
||||
return (uuidString != null) ? UUID.fromString(uuidString) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
|
||||
// CallableStatement에서 값을 가져와 Java UUID 객체로 변환할 때
|
||||
String uuidString = cs.getString(columnIndex);
|
||||
return (uuidString != null) ? UUID.fromString(uuidString) : null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
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 경로
|
||||
public class AdminSurveyPageController {
|
||||
|
||||
/**
|
||||
* 설문 생성 폼 페이지를 표시합니다.
|
||||
* 웹 브라우저에서 `/admin/survey/createSurveyForm.do` 로 접근 시 이 메서드가 호출됩니다.
|
||||
*
|
||||
* @return JSP 파일의 논리적 경로 (ViewResolver에 의해 실제 경로로 변환됨)
|
||||
*/
|
||||
@GetMapping("/createSurveyForm.do") // GET 요청 처리, .do 확장자 추가
|
||||
public String showCreateSurveyForm() {
|
||||
// ViewResolver 설정에 따라 다음 경로의 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";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,146 @@
|
|||
package sgis.surveysystem.controller;
|
||||
|
||||
import sgis.surveysystem.dto.QuestionCreateRequest;
|
||||
import sgis.surveysystem.dto.QuestionResponse;
|
||||
import sgis.surveysystem.dto.QuestionUpdateRequest;
|
||||
import sgis.surveysystem.dto.QuestionOptionResponse; // QuestionResponse에서 옵션을 포함하기 위해 필요
|
||||
import sgis.surveysystem.service.QuestionService;
|
||||
import sgis.surveysystem.service.QuestionOptionService; // 문항 옵션을 가져오기 위해 주입
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 설문 문항(Question) 관련 HTTP 요청을 처리하는 REST 컨트롤러.
|
||||
* 문항 생성, 조회, 수정, 삭제 등의 API 엔드포인트를 제공합니다.
|
||||
* 모든 요청은 .do 확장자로 끝나며, GET과 POST 메서드만 사용합니다.
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/questions")
|
||||
@RequiredArgsConstructor
|
||||
public class QuestionController {
|
||||
|
||||
private final QuestionService questionService;
|
||||
private final QuestionOptionService questionOptionService; // 객관식 문항의 옵션을 로드하기 위해 필요
|
||||
|
||||
/**
|
||||
* 새로운 설문 문항을 생성합니다. (HTTP POST)
|
||||
*
|
||||
* @param request Question 생성 요청 DTO
|
||||
* @return 생성된 문항 정보를 담은 응답 (HTTP 201 Created)
|
||||
*/
|
||||
@PostMapping("/createQuestion.do") // POST 요청 처리, .do 확장자 추가
|
||||
public ResponseEntity<QuestionResponse> createQuestion(@RequestBody QuestionCreateRequest request) {
|
||||
QuestionResponse createdQuestion = QuestionResponse.from(
|
||||
questionService.createQuestion(
|
||||
request.getSurveyId(),
|
||||
request.getQuestionText(),
|
||||
request.getQuestionType(),
|
||||
request.getQuestionOrder(),
|
||||
request.getIsRequired()
|
||||
)
|
||||
);
|
||||
return new ResponseEntity<>(createdQuestion, HttpStatus.CREATED);
|
||||
}
|
||||
|
||||
/**
|
||||
* 특정 설문에 속한 모든 문항을 조회합니다. (HTTP GET)
|
||||
* 문항 유형이 객관식인 경우, 해당 문항의 옵션도 함께 포함하여 반환합니다.
|
||||
*
|
||||
* @param surveyId 문항 목록을 조회할 설문 ID
|
||||
* @return 해당 설문의 모든 문항 정보를 담은 응답 리스트 (HTTP 200 OK)
|
||||
*/
|
||||
@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 -> {
|
||||
QuestionResponse questionResponse = QuestionResponse.from(question);
|
||||
// 문항 유형이 객관식인 경우, 해당 문항의 옵션을 조회하여 DTO에 설정합니다.
|
||||
if (question.getQuestionType().startsWith("객관식")) {
|
||||
List<QuestionOptionResponse> options = questionOptionService.getOptionsByQuestionId(question.getQuestionId()).stream()
|
||||
.map(QuestionOptionResponse::from)
|
||||
.collect(Collectors.toList());
|
||||
questionResponse.setOptions(options); // QuestionResponse DTO에 옵션 리스트 설정
|
||||
}
|
||||
return questionResponse;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
return ResponseEntity.ok(questions);
|
||||
}
|
||||
|
||||
/**
|
||||
* 특정 ID의 문항을 조회합니다. (HTTP GET)
|
||||
* 문항 유형이 객관식인 경우, 해당 문항의 옵션도 함께 포함하여 반환합니다.
|
||||
*
|
||||
* @param questionId 조회할 문항의 고유 ID
|
||||
* @return 조회된 문항 정보를 담은 응답 (HTTP 200 OK)
|
||||
* 문항이 없을 경우 HTTP 404 Not Found 반환
|
||||
*/
|
||||
@GetMapping("/{questionId}/getQuestion.do") // GET 요청 처리, .do 확장자 추가
|
||||
public ResponseEntity<QuestionResponse> getQuestionById(@PathVariable UUID questionId) {
|
||||
try {
|
||||
QuestionResponse question = QuestionResponse.from(questionService.getQuestionById(questionId));
|
||||
// 문항 유형이 객관식인 경우, 해당 문항의 옵션을 조회하여 DTO에 설정합니다.
|
||||
if (question.getQuestionType().startsWith("객관식")) {
|
||||
List<QuestionOptionResponse> options = questionOptionService.getOptionsByQuestionId(question.getQuestionId()).stream()
|
||||
.map(QuestionOptionResponse::from)
|
||||
.collect(Collectors.toList());
|
||||
question.setOptions(options); // QuestionResponse DTO에 옵션 리스트 설정
|
||||
}
|
||||
return ResponseEntity.ok(question);
|
||||
} catch (NoSuchElementException e) {
|
||||
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 문항 정보를 업데이트합니다. (HTTP POST)
|
||||
* PUT 요청 대신 POST를 사용하며, URL 경로에 update 액션을 명시합니다.
|
||||
*
|
||||
* @param questionId 업데이트할 문항의 고유 ID
|
||||
* @param request Question 업데이트 요청 DTO
|
||||
* @return 업데이트된 문항 정보를 담은 응답 (HTTP 200 OK)
|
||||
* 문항이 없을 경우 HTTP 404 Not Found 반환
|
||||
*/
|
||||
@PostMapping("/{questionId}/updateQuestion.do") // POST 요청으로 업데이트 처리, .do 확장자 추가
|
||||
public ResponseEntity<QuestionResponse> updateQuestion(@PathVariable UUID questionId, @RequestBody QuestionUpdateRequest request) {
|
||||
try {
|
||||
QuestionResponse updatedQuestion = QuestionResponse.from(
|
||||
questionService.updateQuestion(
|
||||
questionId,
|
||||
request.getQuestionText(),
|
||||
request.getQuestionType(),
|
||||
request.getQuestionOrder(),
|
||||
request.getIsRequired()
|
||||
)
|
||||
);
|
||||
return ResponseEntity.ok(updatedQuestion);
|
||||
} catch (NoSuchElementException e) {
|
||||
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 특정 ID의 문항을 삭제합니다. (HTTP POST)
|
||||
* DELETE 요청 대신 POST를 사용하며, URL 경로에 delete 액션을 명시합니다.
|
||||
*
|
||||
* @param questionId 삭제할 문항의 고유 ID
|
||||
* @return 응답 본문 없이 HTTP 204 No Content 반환
|
||||
* 문항이 없을 경우 HTTP 404 Not Found 반환
|
||||
*/
|
||||
@PostMapping("/{questionId}/deleteQuestion.do") // POST 요청으로 삭제 처리, .do 확장자 추가
|
||||
public ResponseEntity<Void> deleteQuestion(@PathVariable UUID questionId) {
|
||||
try {
|
||||
questionService.deleteQuestion(questionId);
|
||||
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
|
||||
} catch (NoSuchElementException e) {
|
||||
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
package sgis.surveysystem.controller;
|
||||
|
||||
import sgis.surveysystem.dto.QuestionOptionCreateRequest;
|
||||
import sgis.surveysystem.dto.QuestionOptionResponse;
|
||||
import sgis.surveysystem.dto.QuestionOptionUpdateRequest;
|
||||
import sgis.surveysystem.service.QuestionOptionService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 객관식 문항 선택지(QuestionOption) 관련 HTTP 요청을 처리하는 REST 컨트롤러.
|
||||
* 선택지 생성, 조회, 수정, 삭제 등의 API 엔드포인트를 제공합니다.
|
||||
* 모든 요청은 .do 확장자로 끝나며, GET과 POST 메서드만 사용합니다.
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/question-options")
|
||||
@RequiredArgsConstructor
|
||||
public class QuestionOptionController {
|
||||
|
||||
private final QuestionOptionService questionOptionService;
|
||||
|
||||
/**
|
||||
* 새로운 객관식 문항 선택지를 생성합니다. (HTTP POST)
|
||||
*
|
||||
* @param request QuestionOption 생성 요청 DTO
|
||||
* @return 생성된 선택지 정보를 담은 응답 (HTTP 201 Created)
|
||||
*/
|
||||
@PostMapping("/createQuestionOption.do") // POST 요청 처리, .do 확장자 추가
|
||||
public ResponseEntity<QuestionOptionResponse> createQuestionOption(@RequestBody QuestionOptionCreateRequest request) {
|
||||
QuestionOptionResponse createdOption = QuestionOptionResponse.from(
|
||||
questionOptionService.createQuestionOption(
|
||||
request.getQuestionId(),
|
||||
request.getOptionText(),
|
||||
request.getOptionOrder()
|
||||
)
|
||||
);
|
||||
return new ResponseEntity<>(createdOption, HttpStatus.CREATED);
|
||||
}
|
||||
|
||||
/**
|
||||
* 특정 문항에 속한 모든 선택지를 조회합니다. (HTTP GET)
|
||||
*
|
||||
* @param questionId 선택지 목록을 조회할 문항 ID
|
||||
* @return 해당 문항의 모든 선택지 정보를 담은 응답 리스트 (HTTP 200 OK)
|
||||
*/
|
||||
@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)
|
||||
.collect(Collectors.toList());
|
||||
return ResponseEntity.ok(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* 특정 ID의 선택지를 조회합니다. (HTTP GET)
|
||||
*
|
||||
* @param optionId 조회할 선택지의 고유 ID
|
||||
* @return 조회된 선택지 정보를 담은 응답 (HTTP 200 OK)
|
||||
* 선택지가 없을 경우 HTTP 404 Not Found 반환
|
||||
*/
|
||||
@GetMapping("/{optionId}/getOption.do") // GET 요청 처리, .do 확장자 추가
|
||||
public ResponseEntity<QuestionOptionResponse> getQuestionOptionById(@PathVariable UUID optionId) {
|
||||
try {
|
||||
QuestionOptionResponse option = QuestionOptionResponse.from(questionOptionService.getQuestionOptionById(optionId));
|
||||
return ResponseEntity.ok(option);
|
||||
} catch (NoSuchElementException e) {
|
||||
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 선택지 정보를 업데이트합니다. (HTTP POST)
|
||||
* PUT 요청 대신 POST를 사용하며, URL 경로에 update 액션을 명시합니다.
|
||||
*
|
||||
* @param optionId 업데이트할 선택지의 고유 ID
|
||||
* @param request QuestionOption 업데이트 요청 DTO
|
||||
* @return 업데이트된 선택지 정보를 담은 응답 (HTTP 200 OK)
|
||||
* 선택지가 없을 경우 HTTP 404 Not Found 반환
|
||||
*/
|
||||
@PostMapping("/{optionId}/updateOption.do") // POST 요청으로 업데이트 처리, .do 확장자 추가
|
||||
public ResponseEntity<QuestionOptionResponse> updateQuestionOption(@PathVariable UUID optionId, @RequestBody QuestionOptionUpdateRequest request) {
|
||||
try {
|
||||
QuestionOptionResponse updatedOption = QuestionOptionResponse.from(
|
||||
questionOptionService.updateQuestionOption(
|
||||
optionId,
|
||||
request.getOptionText(),
|
||||
request.getOptionOrder()
|
||||
)
|
||||
);
|
||||
return ResponseEntity.ok(updatedOption);
|
||||
} catch (NoSuchElementException e) {
|
||||
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 특정 ID의 선택지를 삭제합니다. (HTTP POST)
|
||||
* DELETE 요청 대신 POST를 사용하며, URL 경로에 delete 액션을 명시합니다.
|
||||
*
|
||||
* @param optionId 삭제할 선택지의 고유 ID
|
||||
* @return 응답 본문 없이 HTTP 204 No Content 반환
|
||||
* 선택지가 없을 경우 HTTP 404 Not Found 반환
|
||||
*/
|
||||
@PostMapping("/{optionId}/deleteOption.do") // POST 요청으로 삭제 처리, .do 확장자 추가
|
||||
public ResponseEntity<Void> deleteQuestionOption(@PathVariable UUID optionId) {
|
||||
try {
|
||||
questionOptionService.deleteQuestionOption(optionId);
|
||||
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
|
||||
} catch (NoSuchElementException e) {
|
||||
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,163 @@
|
|||
package sgis.surveysystem.controller;
|
||||
|
||||
import sgis.surveysystem.dto.SurveyCreateRequest;
|
||||
import sgis.surveysystem.dto.SurveyResponse;
|
||||
import sgis.surveysystem.dto.SurveyUpdateRequest;
|
||||
import sgis.surveysystem.service.SurveyService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
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;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 설문(Survey) 관련 HTTP 요청을 처리하는 REST 컨트롤러.
|
||||
* 설문 생성, 조회, 수정, 삭제 등의 API 엔드포인트를 제공합니다.
|
||||
* 모든 요청은 .do 확장자로 끝나며, GET과 POST 메서드만 사용합니다.
|
||||
*/
|
||||
@RestController // RESTful 웹 서비스 컨트롤러임을 선언
|
||||
@RequestMapping("/api/surveys") // 기본 URL 경로 (이 부분은 .do를 포함하지 않음, 각 메서드에서 .do를 추가)
|
||||
@RequiredArgsConstructor // Lombok을 사용하여 final 필드를 인자로 받는 생성자 자동 생성 (SurveyService 주입)
|
||||
public class SurveyController {
|
||||
|
||||
private final SurveyService surveyService;
|
||||
|
||||
/**
|
||||
* 새로운 설문을 생성합니다. (HTTP POST)
|
||||
* 요청 본문에서 설문 정보를 받아 서비스를 통해 저장합니다.
|
||||
*
|
||||
* @param request Survey 생성 요청 DTO
|
||||
* @return 생성된 설문 정보를 담은 응답 (HTTP 201 Created)
|
||||
*/
|
||||
@PostMapping("/createSurvey.do")
|
||||
public ResponseEntity<SurveyResponse> createSurvey(@RequestBody SurveyCreateRequest request) {
|
||||
|
||||
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(),
|
||||
startDate, // 파싱된 LocalDateTime 객체 전달
|
||||
endDate // 파싱된 LocalDateTime 객체 전달
|
||||
)
|
||||
);
|
||||
return new ResponseEntity<>(createdSurvey, HttpStatus.CREATED);
|
||||
}
|
||||
|
||||
/**
|
||||
* 모든 설문 목록을 조회합니다. (HTTP GET)
|
||||
*
|
||||
* @return 모든 설문 정보를 담은 응답 리스트 (HTTP 200 OK)
|
||||
*/
|
||||
@GetMapping("/getAllSurveys.do") // GET 요청 처리, .do 확장자 추가
|
||||
public ResponseEntity<List<SurveyResponse>> getAllSurveys() {
|
||||
// 서비스 계층에서 모든 설문을 조회하고, 각 설문 Entity를 SurveyResponse DTO로 변환합니다.
|
||||
List<SurveyResponse> surveys = surveyService.getAllSurveys().stream()
|
||||
.map(SurveyResponse::from)
|
||||
.collect(Collectors.toList());
|
||||
// 조회 성공 시 200 OK 상태 코드와 함께 설문 목록을 반환합니다.
|
||||
return ResponseEntity.ok(surveys);
|
||||
}
|
||||
|
||||
/**
|
||||
* 특정 ID의 설문을 조회합니다. (HTTP GET)
|
||||
* URL 경로 변수(PathVariable)를 통해 설문 ID를 받습니다.
|
||||
*
|
||||
* @param surveyId 조회할 설문의 고유 ID
|
||||
* @return 조회된 설문 정보를 담은 응답 (HTTP 200 OK)
|
||||
* 설문이 없을 경우 HTTP 404 Not Found 반환
|
||||
*/
|
||||
@GetMapping("/{surveyId}/getSurvey.do") // {surveyId}는 URL 경로의 변수, .do 확장자 추가
|
||||
public ResponseEntity<SurveyResponse> getSurveyById(@PathVariable UUID surveyId) {
|
||||
try {
|
||||
// 서비스 계층에서 ID로 설문을 조회하고, Entity를 SurveyResponse DTO로 변환합니다.
|
||||
SurveyResponse survey = SurveyResponse.from(surveyService.getSurveyById(surveyId));
|
||||
return ResponseEntity.ok(survey);
|
||||
} catch (NoSuchElementException e) {
|
||||
// 해당 ID의 설문을 찾을 수 없을 경우 404 Not Found 상태 코드를 반환합니다.
|
||||
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 설문 정보를 업데이트합니다. (HTTP POST)
|
||||
* PUT 요청 대신 POST를 사용하며, URL 경로에 update 액션을 명시합니다.
|
||||
*
|
||||
* @param surveyId 업데이트할 설문의 고유 ID
|
||||
* @param request Survey 업데이트 요청 DTO
|
||||
* @return 업데이트된 설문 정보를 담은 응답 (HTTP 200 OK)
|
||||
* 설문이 없을 경우 HTTP 404 Not Found 반환
|
||||
*/
|
||||
@PostMapping("/{surveyId}/updateSurvey.do") // POST 요청으로 업데이트 처리, .do 확장자 추가
|
||||
public ResponseEntity<SurveyResponse> updateSurvey(@PathVariable UUID surveyId, @RequestBody SurveyUpdateRequest request) {
|
||||
try {
|
||||
// 서비스 계층의 updateSurvey 메서드를 호출하여 설문을 업데이트합니다.
|
||||
SurveyResponse updatedSurvey = SurveyResponse.from(
|
||||
surveyService.updateSurvey(
|
||||
surveyId,
|
||||
request.getSurveyTitle(),
|
||||
request.getSurveyDescription(), // 수정: surveydescription() -> getSurveyDescription()
|
||||
request.getIsActive(),
|
||||
request.getStartDate(),
|
||||
request.getEndDate()
|
||||
)
|
||||
);
|
||||
return ResponseEntity.ok(updatedSurvey);
|
||||
} catch (NoSuchElementException e) {
|
||||
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 특정 ID의 설문을 삭제합니다. (HTTP POST)
|
||||
* DELETE 요청 대신 POST를 사용하며, URL 경로에 delete 액션을 명시합니다.
|
||||
*
|
||||
* @param surveyId 삭제할 설문의 고유 ID
|
||||
* @return 응답 본문 없이 HTTP 204 No Content 반환
|
||||
* 설문이 없을 경우 HTTP 404 Not Found 반환
|
||||
*/
|
||||
@PostMapping("/{surveyId}/deleteSurvey.do") // POST 요청으로 삭제 처리, .do 확장자 추가
|
||||
public ResponseEntity<Void> deleteSurvey(@PathVariable UUID surveyId) {
|
||||
try {
|
||||
// 서비스 계층의 deleteSurvey 메서드를 호출하여 설문을 삭제합니다.
|
||||
surveyService.deleteSurvey(surveyId);
|
||||
// 삭제 성공 시 204 No Content 상태 코드를 반환합니다. (응답 본문 없음)
|
||||
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
|
||||
} catch (NoSuchElementException e) {
|
||||
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 특정 ID의 설문을 비활성화합니다. (HTTP POST)
|
||||
* PATCH 요청 대신 POST를 사용하며, URL 경로에 deactivate 액션을 명시합니다.
|
||||
*
|
||||
* @param surveyId 비활성화할 설문의 고유 ID
|
||||
* @return 비활성화된 설문 정보를 담은 응답 (HTTP 200 OK)
|
||||
* 설문이 없을 경우 HTTP 404 Not Found 반환
|
||||
*/
|
||||
@PostMapping("/{surveyId}/deactivateSurvey.do") // POST 요청으로 비활성화 처리, .do 확장자 추가
|
||||
public ResponseEntity<SurveyResponse> deactivateSurvey(@PathVariable UUID surveyId) {
|
||||
try {
|
||||
// 서비스 계층의 deactivateSurvey 메서드를 호출하여 설문을 비활성화합니다.
|
||||
SurveyResponse deactivatedSurvey = SurveyResponse.from(surveyService.deactivateSurvey(surveyId));
|
||||
return ResponseEntity.ok(deactivatedSurvey);
|
||||
} catch (NoSuchElementException e) {
|
||||
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package sgis.surveysystem.domain;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
public class Answer {
|
||||
private UUID answerId;
|
||||
private UUID responseId; // FK: tb_survey_responses.response_id
|
||||
private UUID questionId; // FK: tb_questions.question_id
|
||||
private String answerText;
|
||||
private UUID selectedOptionId; // FK: tb_question_options.option_id (단일 선택용)
|
||||
|
||||
@Builder
|
||||
public Answer(UUID answerId, UUID responseId, UUID questionId, String answerText, UUID selectedOptionId) {
|
||||
this.answerId = answerId;
|
||||
this.responseId = responseId;
|
||||
this.questionId = questionId;
|
||||
this.answerText = answerText;
|
||||
this.selectedOptionId = selectedOptionId;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package sgis.surveysystem.domain;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class AnswerSelectedOption {
|
||||
private UUID answerId; // FK: tb_answers.answer_id (복합 PK의 일부)
|
||||
private UUID optionId; // FK: tb_question_options.option_id (복합 PK의 일부)
|
||||
|
||||
@Builder
|
||||
public AnswerSelectedOption(UUID answerId, UUID optionId) {
|
||||
this.answerId = answerId;
|
||||
this.optionId = optionId;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package sgis.surveysystem.domain;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class Question {
|
||||
private UUID questionId;
|
||||
private UUID surveyId; // FK: tb_surveys.survey_id
|
||||
private String questionText;
|
||||
private String questionType;
|
||||
private Integer questionOrder;
|
||||
private Boolean isRequired;
|
||||
|
||||
@Builder
|
||||
public Question(UUID questionId, UUID surveyId, String questionText, String questionType, Integer questionOrder, Boolean isRequired) {
|
||||
this.questionId = questionId;
|
||||
this.surveyId = surveyId;
|
||||
this.questionText = questionText;
|
||||
this.questionType = questionType;
|
||||
this.questionOrder = questionOrder;
|
||||
this.isRequired = isRequired;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package sgis.surveysystem.domain;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class QuestionOption {
|
||||
private UUID optionId;
|
||||
private UUID questionId; // FK: tb_questions.question_id
|
||||
private String optionText;
|
||||
private Integer optionOrder;
|
||||
|
||||
@Builder
|
||||
public QuestionOption(UUID optionId, UUID questionId, String optionText, Integer optionOrder) {
|
||||
this.optionId = optionId;
|
||||
this.questionId = questionId;
|
||||
this.optionText = optionText;
|
||||
this.optionOrder = optionOrder;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package sgis.surveysystem.domain;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class Respondent {
|
||||
private UUID respondentId;
|
||||
private String userId;
|
||||
private String ipAddress; // INET 타입은 String으로 매핑 (MyBatis에서 별도 타입 핸들러 필요할 수 있음)
|
||||
private LocalDateTime responseStartAt;
|
||||
private LocalDateTime responseEndAt;
|
||||
|
||||
@Builder
|
||||
public Respondent(UUID respondentId, String userId, String ipAddress, LocalDateTime responseStartAt, LocalDateTime responseEndAt) {
|
||||
this.respondentId = respondentId;
|
||||
this.userId = userId;
|
||||
this.ipAddress = ipAddress;
|
||||
this.responseStartAt = responseStartAt;
|
||||
this.responseEndAt = responseEndAt;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
package sgis.surveysystem.domain;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter; // MyBatis는 매핑 시 Setter를 사용하기도 함
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
@Getter
|
||||
@Setter // MyBatis 매핑을 위해 필요할 수 있음
|
||||
@NoArgsConstructor // 기본 생성자
|
||||
@Builder
|
||||
public class Survey {
|
||||
private UUID surveyId;
|
||||
private String surveyTitle;
|
||||
private String surveyDescription;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
private Boolean isActive;
|
||||
private LocalDateTime startDate;
|
||||
private LocalDateTime endDate;
|
||||
|
||||
@Builder // 빌더 패턴을 위한 어노테이션
|
||||
public Survey(UUID surveyId, String surveyTitle, String surveyDescription, LocalDateTime createdAt, LocalDateTime updatedAt, Boolean isActive, LocalDateTime startDate, LocalDateTime endDate) {
|
||||
this.surveyId = surveyId;
|
||||
this.surveyTitle = surveyTitle;
|
||||
this.surveyDescription = surveyDescription;
|
||||
this.createdAt = createdAt;
|
||||
this.updatedAt = updatedAt;
|
||||
this.isActive = isActive;
|
||||
this.startDate = startDate;
|
||||
this.endDate = endDate;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package sgis.surveysystem.domain;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class SurveyResponse {
|
||||
private UUID responseId;
|
||||
private UUID surveyId; // FK: tb_surveys.survey_id
|
||||
private UUID respondentId; // FK: tb_respondents.respondent_id
|
||||
private LocalDateTime submittedAt;
|
||||
|
||||
@Builder
|
||||
public SurveyResponse(UUID responseId, UUID surveyId, UUID respondentId, LocalDateTime submittedAt) {
|
||||
this.responseId = responseId;
|
||||
this.surveyId = surveyId;
|
||||
this.respondentId = respondentId;
|
||||
this.submittedAt = submittedAt;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package sgis.surveysystem.dto;
|
||||
|
||||
import sgis.surveysystem.domain.Answer;
|
||||
import sgis.surveysystem.domain.AnswerSelectedOption; // 다중 선택 옵션 DTO 변환을 위해 필요
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
// 답변 생성 요청 DTO (단일/주관식/다중 선택 모두 커버)
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class AnswerCreateRequest {
|
||||
private UUID responseId;
|
||||
private UUID questionId;
|
||||
private String answerText; // 단답형, 주관식용
|
||||
private UUID selectedOptionId; // 객관식 단일 선택용
|
||||
private List<UUID> selectedOptionIds; // 객관식 다중 선택용
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package sgis.surveysystem.dto;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import sgis.surveysystem.domain.Answer;
|
||||
|
||||
//답변 응답 DTO (다중 선택 옵션 포함)
|
||||
@Getter
|
||||
@Setter // AnswerSelectedOptionResponse 리스트를 설정하기 위해 Setter 필요
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class AnswerResponse {
|
||||
private UUID answerId;
|
||||
private UUID responseId;
|
||||
private UUID questionId;
|
||||
private String answerText;
|
||||
private UUID selectedOptionId;
|
||||
private List<AnswerSelectedOptionResponse> selectedOptions; // 다중 선택된 옵션 목록
|
||||
|
||||
public static AnswerResponse from(Answer answer) {
|
||||
if (answer == null) {
|
||||
return null;
|
||||
}
|
||||
return AnswerResponse.builder()
|
||||
.answerId(answer.getAnswerId())
|
||||
.responseId(answer.getResponseId())
|
||||
.questionId(answer.getQuestionId())
|
||||
.answerText(answer.getAnswerText())
|
||||
.selectedOptionId(answer.getSelectedOptionId())
|
||||
// selectedOptions는 서비스/컨트롤러에서 AnswerSelectedOptionService를 통해 채웁니다.
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package sgis.surveysystem.dto;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
//다중 선택 옵션 추가 요청 DTO
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class AnswerSelectedOptionCreateRequest {
|
||||
private UUID answerId; // 어떤 Answer에 속하는지
|
||||
private UUID optionId; // 선택된 옵션 ID
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package sgis.surveysystem.dto;
|
||||
|
||||
import sgis.surveysystem.domain.AnswerSelectedOption;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
|
||||
|
||||
// 다중 선택 옵션 응답 DTO
|
||||
@Getter
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class AnswerSelectedOptionResponse {
|
||||
private UUID answerId;
|
||||
private UUID optionId;
|
||||
|
||||
public static AnswerSelectedOptionResponse from(AnswerSelectedOption answerSelectedOption) {
|
||||
if (answerSelectedOption == null) {
|
||||
return null;
|
||||
}
|
||||
return AnswerSelectedOptionResponse.builder()
|
||||
.answerId(answerSelectedOption.getAnswerId())
|
||||
.optionId(answerSelectedOption.getOptionId())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package sgis.surveysystem.dto;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
//답변 업데이트 요청 DTO (주로 단답/주관식/단일 선택용)
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class AnswerUpdateRequest {
|
||||
private String answerText;
|
||||
private UUID selectedOptionId;
|
||||
// 다중 선택 옵션 업데이트는 AnswerSelectedOption 관련 별도 요청 사용 권장
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package sgis.surveysystem.dto;
|
||||
|
||||
import sgis.surveysystem.domain.Question;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
// 문항 생성 요청 DTO
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class QuestionCreateRequest {
|
||||
private UUID surveyId; // 어떤 설문에 속하는지
|
||||
private String questionText;
|
||||
private String questionType;
|
||||
private Integer questionOrder;
|
||||
private Boolean isRequired;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package sgis.surveysystem.dto;
|
||||
|
||||
import sgis.surveysystem.domain.QuestionOption;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
// 선택지 생성 요청 DTO
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class QuestionOptionCreateRequest {
|
||||
private UUID questionId; // 어떤 문항에 속하는지
|
||||
private String optionText;
|
||||
private Integer optionOrder;
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
package sgis.surveysystem.dto;
|
||||
|
||||
import sgis.surveysystem.domain.QuestionOption;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
|
||||
// 선택지 응답 DTO
|
||||
@Getter
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class QuestionOptionResponse {
|
||||
private UUID optionId;
|
||||
private UUID questionId; // 어떤 문항에 속하는지
|
||||
private String optionText;
|
||||
private Integer optionOrder;
|
||||
|
||||
public static QuestionOptionResponse from(QuestionOption option) {
|
||||
if (option == null) {
|
||||
return null;
|
||||
}
|
||||
return QuestionOptionResponse.builder()
|
||||
.optionId(option.getOptionId())
|
||||
.questionId(option.getQuestionId())
|
||||
.optionText(option.getOptionText())
|
||||
.optionOrder(option.getOptionOrder())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package sgis.surveysystem.dto;
|
||||
|
||||
import sgis.surveysystem.domain.QuestionOption;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
|
||||
// 선택지 업데이트 요청 DTO
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class QuestionOptionUpdateRequest {
|
||||
private String optionText;
|
||||
private Integer optionOrder;
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
package sgis.surveysystem.dto;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import sgis.surveysystem.domain.Question;
|
||||
|
||||
//문항 응답 DTO (선택지 포함 가능)
|
||||
@Getter
|
||||
@Setter // Setter가 필요한 경우 (예: Controller에서 옵션 리스트를 동적으로 설정)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class QuestionResponse {
|
||||
private UUID questionId;
|
||||
private UUID surveyId; // 어떤 설문에 속하는지
|
||||
private String questionText;
|
||||
private String questionType;
|
||||
private Integer questionOrder;
|
||||
private Boolean isRequired;
|
||||
private List<QuestionOptionResponse> options; // 객관식 문항의 선택지 리스트 (서비스/컨트롤러에서 채움)
|
||||
|
||||
public static QuestionResponse from(Question question) {
|
||||
if (question == null) {
|
||||
return null;
|
||||
}
|
||||
return QuestionResponse.builder()
|
||||
.questionId(question.getQuestionId())
|
||||
.surveyId(question.getSurveyId())
|
||||
.questionText(question.getQuestionText())
|
||||
.questionType(question.getQuestionType())
|
||||
.questionOrder(question.getQuestionOrder())
|
||||
.isRequired(question.getIsRequired())
|
||||
// options 필드는 기본적으로 null로 두고, Controller/Service에서 필요한 경우 채웁니다.
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package sgis.surveysystem.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
//문항 업데이트 요청 DTO
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class QuestionUpdateRequest {
|
||||
private String questionText;
|
||||
private String questionType;
|
||||
private Integer questionOrder;
|
||||
private Boolean isRequired;
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package sgis.surveysystem.dto;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
|
||||
// 응답자 생성 요청 DTO (회원용/익명용)
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class RespondentCreateRequest {
|
||||
private String userId; // 시스템 내 사용자 ID (회원인 경우)
|
||||
private String ipAddress; // 응답자의 IP 주소
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package sgis.surveysystem.dto;
|
||||
|
||||
import sgis.surveysystem.domain.Respondent;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
|
||||
// 응답자 응답 DTO
|
||||
@Getter
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class RespondentResponse {
|
||||
private UUID respondentId;
|
||||
private String userId;
|
||||
private String ipAddress;
|
||||
private LocalDateTime responseStartAt;
|
||||
private LocalDateTime responseEndAt;
|
||||
|
||||
public static RespondentResponse from(Respondent respondent) {
|
||||
if (respondent == null) {
|
||||
return null;
|
||||
}
|
||||
return RespondentResponse.builder()
|
||||
.respondentId(respondent.getRespondentId())
|
||||
.userId(respondent.getUserId())
|
||||
.ipAddress(respondent.getIpAddress())
|
||||
.responseStartAt(respondent.getResponseStartAt())
|
||||
.responseEndAt(respondent.getResponseEndAt())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package sgis.surveysystem.dto;
|
||||
|
||||
import sgis.surveysystem.domain.Respondent;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
|
||||
// 응답자 업데이트 요청 DTO (주로 응답 완료 시간 업데이트 등에 사용)
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class RespondentUpdateRequest {
|
||||
private String userId;
|
||||
private String ipAddress;
|
||||
private LocalDateTime responseEndAt;
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package sgis.surveysystem.dto;
|
||||
|
||||
import sgis.surveysystem.domain.Survey; // 도메인 객체를 DTO로 변환하기 위해 필요
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.AllArgsConstructor; // 모든 필드를 인자로 받는 생성자 추가 (선택 사항이지만 유용)
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
// 설문 생성 요청 DTO: 클라이언트에서 설문 생성 시 보내는 데이터
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor // 요청 DTO는 모든 필드 생성자도 유용
|
||||
public class SurveyCreateRequest {
|
||||
private String surveyTitle;
|
||||
private String surveyDescription;
|
||||
//private LocalDateTime startDate;
|
||||
//private LocalDateTime endDate;
|
||||
private String startDate;
|
||||
private String endDate;
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
package sgis.surveysystem.dto;
|
||||
|
||||
import sgis.surveysystem.domain.Survey; // Import Survey domain object
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
|
||||
// Survey 응답 DTO: 서버에서 클라이언트로 설문 정보를 보낼 때 사용하는 데이터
|
||||
@Getter
|
||||
@Setter // DTO가 데이터를 받을 때 Setter가 필요할 수 있음
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor // Lombok이 모든 필드를 인자로 받는 생성자를 생성
|
||||
@Builder // <--- 클래스 레벨에 @Builder 어노테이션
|
||||
public class SurveyResponse {
|
||||
private UUID surveyId;
|
||||
private String surveyTitle;
|
||||
private String surveyDescription;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
private Boolean isActive;
|
||||
private LocalDateTime startDate;
|
||||
private LocalDateTime endDate;
|
||||
|
||||
// Entity (도메인 객체)를 DTO로 변환하는 정적 팩토리 메서드
|
||||
public static SurveyResponse from(Survey survey) {
|
||||
if (survey == null) {
|
||||
return null;
|
||||
}
|
||||
return SurveyResponse.builder() // <--- 이 부분이 이제 정상적으로 호출될 것임
|
||||
.surveyId(survey.getSurveyId())
|
||||
.surveyTitle(survey.getSurveyTitle())
|
||||
.surveyDescription(survey.getSurveyDescription())
|
||||
.createdAt(survey.getCreatedAt())
|
||||
.updatedAt(survey.getUpdatedAt())
|
||||
.isActive(survey.getIsActive())
|
||||
.startDate(survey.getStartDate())
|
||||
.endDate(survey.getEndDate())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package sgis.surveysystem.dto;
|
||||
|
||||
import sgis.surveysystem.domain.SurveyResponse;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
// 설문 응답 세션 생성 요청 DTO
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class SurveyResponseCreateRequest {
|
||||
private UUID surveyId;
|
||||
private UUID respondentId; // 익명 설문인 경우 null
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
package sgis.surveysystem.dto;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import sgis.surveysystem.domain.SurveyResponse;
|
||||
|
||||
//설문 응답 세션 응답 DTO
|
||||
@Getter
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class SurveyResponseResponse {
|
||||
private UUID responseId;
|
||||
private UUID surveyId;
|
||||
private UUID respondentId;
|
||||
private LocalDateTime submittedAt;
|
||||
|
||||
public static SurveyResponseResponse from(SurveyResponse surveyResponse) {
|
||||
if (surveyResponse == null) {
|
||||
return null;
|
||||
}
|
||||
return SurveyResponseResponse.builder()
|
||||
.responseId(surveyResponse.getResponseId())
|
||||
.surveyId(surveyResponse.getSurveyId())
|
||||
.respondentId(surveyResponse.getRespondentId())
|
||||
.submittedAt(surveyResponse.getSubmittedAt())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package sgis.surveysystem.dto;
|
||||
|
||||
import sgis.surveysystem.domain.Survey; // 도메인 객체를 DTO로 변환하기 위해 필요
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.AllArgsConstructor; // 모든 필드를 인자로 받는 생성자 추가 (선택 사항이지만 유용)
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
|
||||
|
||||
// 설문 업데이트 요청 DTO: 클라이언트에서 설문 업데이트 시 보내는 데이터
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor // 요청 DTO는 모든 필드 생성자도 유용
|
||||
public class SurveyUpdateRequest {
|
||||
private String surveyTitle;
|
||||
private String surveyDescription;
|
||||
private Boolean isActive;
|
||||
private LocalDateTime startDate;
|
||||
private LocalDateTime endDate;
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package sgis.surveysystem.mapper;
|
||||
|
||||
import sgis.surveysystem.domain.Answer;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@Mapper
|
||||
public interface AnswerMapper {
|
||||
void insertAnswer(Answer answer);
|
||||
Optional<Answer> findAnswerById(UUID answerId);
|
||||
List<Answer> findAnswersByResponseId(UUID responseId);
|
||||
List<Answer> findAnswersByQuestionId(UUID questionId);
|
||||
void updateAnswer(Answer answer);
|
||||
void deleteAnswer(UUID answerId);
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package sgis.surveysystem.mapper;
|
||||
|
||||
import sgis.surveysystem.domain.AnswerSelectedOption;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@Mapper
|
||||
public interface AnswerSelectedOptionMapper {
|
||||
void insertAnswerSelectedOption(AnswerSelectedOption answerSelectedOption);
|
||||
void insertMultipleAnswerSelectedOptions(List<AnswerSelectedOption> options); // 다중 선택 옵션 일괄 삽입
|
||||
List<AnswerSelectedOption> findOptionsByAnswerId(UUID answerId);
|
||||
void deleteByAnswerId(UUID answerId); // 특정 답변에 연결된 모든 선택 옵션 삭제
|
||||
void deleteAnswerSelectedOption(@Param("answerId") UUID answerId, @Param("optionId") UUID optionId); // 특정 선택 옵션 삭제
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
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; // Optional은 더 이상 findQuestionById의 반환 타입으로 사용하지 않음
|
||||
import java.util.UUID;
|
||||
|
||||
@Mapper
|
||||
public interface QuestionMapper {
|
||||
void insertQuestion(Question question);
|
||||
|
||||
List<Question> findQuestionsBySurveyId(UUID surveyId);
|
||||
|
||||
Question findQuestionById(UUID questionId);
|
||||
|
||||
void updateQuestion(Question question);
|
||||
|
||||
void deleteQuestion(UUID questionId);
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
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; // Optional은 더 이상 findQuestionOptionById의 반환 타입으로 사용하지 않음
|
||||
import java.util.UUID;
|
||||
|
||||
@Mapper
|
||||
public interface QuestionOptionMapper {
|
||||
void insertQuestionOption(QuestionOption option);
|
||||
|
||||
List<QuestionOption> findOptionsByQuestionId(UUID questionId);
|
||||
QuestionOption findQuestionOptionById(UUID optionId);
|
||||
|
||||
void updateQuestionOption(QuestionOption option);
|
||||
|
||||
void deleteQuestionOption(UUID optionId);
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package sgis.surveysystem.mapper;
|
||||
|
||||
import sgis.surveysystem.domain.Respondent;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface RespondentMapper {
|
||||
void insertRespondent(Respondent respondent);
|
||||
Optional<Respondent> findRespondentById(UUID respondentId);
|
||||
Optional<Respondent> findRespondentByUserId(String userId); // user_id로 응답자 찾기
|
||||
void updateRespondent(Respondent respondent);
|
||||
void deleteRespondent(UUID respondentId);
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package sgis.surveysystem.mapper;
|
||||
|
||||
import sgis.surveysystem.domain.Survey;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional; // Optional은 더 이상 findSurveyById의 반환 타입으로 사용하지 않음
|
||||
import java.util.UUID;
|
||||
|
||||
@Mapper // MyBatis Mapper 임을 선언
|
||||
public interface SurveyMapper {
|
||||
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); // 특정 필드 업데이트
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package sgis.surveysystem.mapper;
|
||||
|
||||
import sgis.surveysystem.domain.SurveyResponse;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@Mapper
|
||||
public interface SurveyResponseMapper {
|
||||
void insertSurveyResponse(SurveyResponse surveyResponse);
|
||||
Optional<SurveyResponse> findSurveyResponseById(UUID responseId);
|
||||
List<SurveyResponse> findResponsesBySurveyId(UUID surveyId);
|
||||
List<SurveyResponse> findResponsesByRespondentId(UUID respondentId);
|
||||
void deleteSurveyResponse(UUID responseId);
|
||||
}
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
package sgis.surveysystem.service;
|
||||
|
||||
import sgis.surveysystem.domain.AnswerSelectedOption;
|
||||
import sgis.surveysystem.mapper.AnswerSelectedOptionMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 다중 선택 객관식 답변의 선택된 옵션(AnswerSelectedOption) 관련 비즈니스 로직을 처리하는 서비스 클래스.
|
||||
* Answer 테이블이 아닌 별도의 테이블에 저장되는 다중 선택 옵션을 관리합니다.
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Transactional
|
||||
public class AnswerSelectedOptionService {
|
||||
|
||||
private final AnswerSelectedOptionMapper answerSelectedOptionMapper;
|
||||
private final AnswerService answerService; // 외래 키 연결 및 답변 유형 확인을 위해 주입
|
||||
private final QuestionOptionService questionOptionService; // 선택지 유효성 검사를 위해 주입
|
||||
|
||||
/**
|
||||
* 다중 선택 객관식 답변에 단일 선택지를 추가합니다.
|
||||
*
|
||||
* @param answerId 답변 고유 ID (해당 답변은 '객관식_다중' 유형이어야 함)
|
||||
* @param optionId 추가할 선택지 ID
|
||||
* @return 생성된 AnswerSelectedOption 객체
|
||||
* @throws IllegalArgumentException 답변 유형이 '객관식_다중'이 아닐 경우
|
||||
* @throws NoSuchElementException 관련 엔티티를 찾을 수 없을 경우
|
||||
*/
|
||||
public AnswerSelectedOption addSelectedOption(UUID answerId, UUID optionId) {
|
||||
// 답변 존재 여부 확인 및 해당 답변이 '객관식_다중' 유형인지 검증
|
||||
// (AnswerService의 getAnswerById를 통해 QuestionService를 거쳐 questionType 확인 필요)
|
||||
answerService.getAnswerById(answerId); // Answer 존재 확인
|
||||
// TODO: 여기에서 answerId에 해당하는 Question의 questionType이 '객관식_다중'인지 확인하는 로직 추가 권장
|
||||
|
||||
questionOptionService.getQuestionOptionById(optionId); // 선택지 존재 여부 확인
|
||||
|
||||
AnswerSelectedOption selectedOption = AnswerSelectedOption.builder()
|
||||
.answerId(answerId)
|
||||
.optionId(optionId)
|
||||
.build();
|
||||
answerSelectedOptionMapper.insertAnswerSelectedOption(selectedOption); // Mapper를 통해 선택 옵션 삽입
|
||||
return selectedOption;
|
||||
}
|
||||
|
||||
/**
|
||||
* 다중 선택 객관식 답변에 여러 선택지를 일괄 추가합니다.
|
||||
*
|
||||
* @param answerId 답변 고유 ID
|
||||
* @param optionIds 추가할 선택지 ID 목록
|
||||
* @throws IllegalArgumentException 답변 유형이 '객관식_다중'이 아닐 경우
|
||||
* @throws NoSuchElementException 관련 엔티티를 찾을 수 없을 경우
|
||||
*/
|
||||
public void addMultipleSelectedOptions(UUID answerId, List<UUID> optionIds) {
|
||||
answerService.getAnswerById(answerId); // Answer 존재 확인
|
||||
// TODO: 여기에서 answerId에 해당하는 Question의 questionType이 '객관식_다중'인지 확인하는 로직 추가 권장
|
||||
|
||||
if (optionIds == null || optionIds.isEmpty()) {
|
||||
return; // 추가할 옵션이 없으면 아무것도 하지 않습니다.
|
||||
}
|
||||
|
||||
List<AnswerSelectedOption> optionsToInsert = new ArrayList<>();
|
||||
for (UUID optionId : optionIds) {
|
||||
questionOptionService.getQuestionOptionById(optionId); // 각 선택지 유효성 검사
|
||||
optionsToInsert.add(AnswerSelectedOption.builder()
|
||||
.answerId(answerId)
|
||||
.optionId(optionId)
|
||||
.build());
|
||||
}
|
||||
answerSelectedOptionMapper.insertMultipleAnswerSelectedOptions(optionsToInsert); // Mapper를 통해 선택 옵션 일괄 삽입
|
||||
}
|
||||
|
||||
/**
|
||||
* 특정 답변에 선택된 모든 옵션을 조회합니다.
|
||||
* 읽기 전용 트랜잭션으로 설정합니다.
|
||||
*
|
||||
* @param answerId 답변 고유 ID
|
||||
* @return 선택된 AnswerSelectedOption 객체 리스트
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
public List<AnswerSelectedOption> getSelectedOptionsByAnswerId(UUID answerId) {
|
||||
answerService.getAnswerById(answerId); // 답변 존재 여부 확인
|
||||
return answerSelectedOptionMapper.findOptionsByAnswerId(answerId); // Mapper를 통해 선택된 옵션 목록 조회
|
||||
}
|
||||
|
||||
/**
|
||||
* 다중 선택 객관식 답변에서 특정 선택지를 제거합니다.
|
||||
*
|
||||
* @param answerId 답변 고유 ID
|
||||
* @param optionId 제거할 선택지 ID
|
||||
*/
|
||||
public void removeSelectedOption(UUID answerId, UUID optionId) {
|
||||
// 삭제 전에 해당 연결이 존재하는지 확인하는 로직을 추가할 수 있습니다.
|
||||
answerSelectedOptionMapper.deleteAnswerSelectedOption(answerId, optionId); // Mapper를 통해 특정 선택 옵션 삭제
|
||||
}
|
||||
|
||||
/**
|
||||
* 특정 답변에 연결된 모든 선택지를 제거합니다.
|
||||
*
|
||||
* @param answerId 답변 고유 ID
|
||||
*/
|
||||
public void removeAllSelectedOptionsByAnswerId(UUID answerId) {
|
||||
answerService.getAnswerById(answerId); // 답변 존재 여부 확인
|
||||
answerSelectedOptionMapper.deleteByAnswerId(answerId); // Mapper를 통해 답변에 연결된 모든 선택 옵션 삭제
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,195 @@
|
|||
package sgis.surveysystem.service;
|
||||
|
||||
import sgis.surveysystem.domain.Answer;
|
||||
import sgis.surveysystem.domain.AnswerSelectedOption;
|
||||
import sgis.surveysystem.mapper.AnswerMapper;
|
||||
import sgis.surveysystem.mapper.AnswerSelectedOptionMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 개별 문항 응답(Answer) 관련 비즈니스 로직을 처리하는 서비스 클래스.
|
||||
* 단답형, 주관식, 객관식 단일/다중 선택 응답을 관리합니다.
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Transactional
|
||||
public class AnswerService {
|
||||
|
||||
private final AnswerMapper answerMapper;
|
||||
private final SurveyResponseService surveyResponseService; // 외래 키 연결을 위해 주입
|
||||
private final QuestionService questionService; // 외래 키 연결 및 문항 유형 확인을 위해 주입
|
||||
private final QuestionOptionService questionOptionService; // 선택지 유효성 검사 및 다중 선택 옵션 처리를 위해 주입
|
||||
private final AnswerSelectedOptionMapper answerSelectedOptionMapper; // 다중 선택 옵션 매퍼 주입
|
||||
|
||||
/**
|
||||
* 새로운 문항 응답을 생성합니다.
|
||||
* 문항 유형에 따라 answerText 또는 selectedOptionId(s)를 저장합니다.
|
||||
*
|
||||
* @param responseId 응답 세션 ID
|
||||
* @param questionId 문항 ID
|
||||
* @param answerText 단답형/주관식 응답 내용 (객관식은 null)
|
||||
* @param selectedOptionId 객관식 단일 선택 시 선택된 옵션 ID (다중 선택/단답/주관식은 null)
|
||||
* @param selectedOptionIds 객관식 다중 선택 시 선택된 옵션 ID 목록 (다른 유형은 null)
|
||||
* @return 생성된 Answer 객체
|
||||
* @throws IllegalArgumentException 유효하지 않은 응답 유형 또는 데이터가 제공될 경우
|
||||
* @throws NoSuchElementException 관련 엔티티(응답 세션, 문항, 선택지)를 찾을 수 없을 경우
|
||||
*/
|
||||
public Answer createAnswer(UUID responseId, UUID questionId, String answerText, UUID selectedOptionId, List<UUID> selectedOptionIds) {
|
||||
surveyResponseService.getSurveyResponseById(responseId); // 응답 세션 존재 여부 확인
|
||||
// 문항 존재 여부 확인 및 문항 유형 가져오기
|
||||
String questionType = questionService.getQuestionById(questionId).getQuestionType();
|
||||
|
||||
Answer answer = Answer.builder()
|
||||
.answerId(UUID.randomUUID()) // 애플리케이션에서 새로운 UUID 생성
|
||||
.responseId(responseId)
|
||||
.questionId(questionId)
|
||||
.build();
|
||||
|
||||
// 문항 유형에 따라 응답 데이터 유효성 검사 및 설정
|
||||
switch (questionType) {
|
||||
case "단답형":
|
||||
case "주관식":
|
||||
if (answerText == null || answerText.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("Short answer or essay questions require text answer.");
|
||||
}
|
||||
answer.setAnswerText(answerText);
|
||||
break;
|
||||
case "객관식_단일":
|
||||
if (selectedOptionId == null) {
|
||||
throw new IllegalArgumentException("Single choice question requires one selected option.");
|
||||
}
|
||||
questionOptionService.getQuestionOptionById(selectedOptionId); // 선택지 존재 여부 확인
|
||||
answer.setSelectedOptionId(selectedOptionId);
|
||||
break;
|
||||
case "객관식_다중":
|
||||
if (selectedOptionIds == null || selectedOptionIds.isEmpty()) {
|
||||
throw new IllegalArgumentException("Multiple choice question requires at least one selected option.");
|
||||
}
|
||||
// Answer 테이블에는 다중 선택 옵션이 직접 저장되지 않으므로, answerText와 selectedOptionId는 null로 유지
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid question type: " + questionType);
|
||||
}
|
||||
|
||||
answerMapper.insertAnswer(answer); // Answer 테이블에 기본 응답 정보 삽입
|
||||
|
||||
// 다중 선택 옵션이 있는 경우 AnswerSelectedOption 테이블에 추가 정보 삽입
|
||||
if ("객관식_다중".equals(questionType) && selectedOptionIds != null && !selectedOptionIds.isEmpty()) {
|
||||
List<AnswerSelectedOption> multiOptions = selectedOptionIds.stream()
|
||||
.map(optionId -> {
|
||||
questionOptionService.getQuestionOptionById(optionId); // 각 선택지 존재 여부 확인
|
||||
return AnswerSelectedOption.builder()
|
||||
.answerId(answer.getAnswerId())
|
||||
.optionId(optionId)
|
||||
.build();
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
answerSelectedOptionMapper.insertMultipleAnswerSelectedOptions(multiOptions); // 일괄 삽입
|
||||
}
|
||||
|
||||
return getAnswerById(answer.getAnswerId()); // 최종적으로 저장된 Answer 객체 반환
|
||||
}
|
||||
|
||||
/**
|
||||
* 특정 ID의 답변을 조회합니다. (객관식 다중 선택 옵션이 있다면 함께 로드)
|
||||
* 읽기 전용 트랜잭션으로 설정합니다.
|
||||
*
|
||||
* @param answerId 답변 고유 ID
|
||||
* @return 조회된 Answer 객체
|
||||
* @throws NoSuchElementException 해당 ID의 답변이 없을 경우
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
public Answer getAnswerById(UUID answerId) {
|
||||
Answer answer = answerMapper.findAnswerById(answerId)
|
||||
.orElseThrow(() -> new NoSuchElementException("Answer not found with ID: " + answerId));
|
||||
|
||||
// 문항 유형이 '객관식_다중'인 경우, AnswerSelectedOption에서 추가 선택지를 로드하여 Answer 객체에 설정합니다.
|
||||
// (Answer 도메인 객체에 List<AnswerSelectedOption> options 필드를 추가하고 Setter를 통해 설정 가능)
|
||||
String questionType = questionService.getQuestionById(answer.getQuestionId()).getQuestionType();
|
||||
if ("객관식_다중".equals(questionType)) {
|
||||
List<AnswerSelectedOption> selectedOptions = answerSelectedOptionMapper.findOptionsByAnswerId(answerId);
|
||||
// 예시: answer.setAnswerSelectedOptions(selectedOptions); // Answer 도메인에 필드 추가 필요
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
|
||||
/**
|
||||
* 특정 응답 세션에 대한 모든 답변을 조회합니다. (각 답변의 다중 선택 옵션도 함께 로드)
|
||||
* 읽기 전용 트랜잭션으로 설정합니다.
|
||||
*
|
||||
* @param responseId 응답 세션 고유 ID
|
||||
* @return 해당 응답 세션의 모든 Answer 객체 리스트
|
||||
* @throws NoSuchElementException 해당 응답 세션 ID가 없을 경우
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
public List<Answer> getAnswersByResponseId(UUID responseId) {
|
||||
surveyResponseService.getSurveyResponseById(responseId); // 응답 세션 존재 여부 확인
|
||||
List<Answer> answers = answerMapper.findAnswersByResponseId(responseId);
|
||||
|
||||
// 각 답변에 대해 다중 선택 옵션을 로드합니다.
|
||||
for (Answer answer : answers) {
|
||||
String questionType = questionService.getQuestionById(answer.getQuestionId()).getQuestionType();
|
||||
if ("객관식_다중".equals(questionType)) {
|
||||
List<AnswerSelectedOption> selectedOptions = answerSelectedOptionMapper.findOptionsByAnswerId(answer.getAnswerId());
|
||||
// 예시: answer.setAnswerSelectedOptions(selectedOptions); // Answer 도메인에 필드 추가 필요
|
||||
}
|
||||
}
|
||||
return answers;
|
||||
}
|
||||
|
||||
/**
|
||||
* 답변을 업데이트합니다. (단답형, 주관식, 객관식 단일 선택만 해당)
|
||||
* 객관식 다중 선택 문항의 옵션 업데이트는 AnswerSelectedOptionService를 통해 별도로 처리해야 합니다.
|
||||
*
|
||||
* @param answerId 업데이트할 답변 고유 ID
|
||||
* @param answerText 새로운 단답형/주관식 내용
|
||||
* @param selectedOptionId 새로운 객관식 단일 선택 옵션 ID
|
||||
* @return 업데이트된 Answer 객체
|
||||
* @throws IllegalArgumentException 유효하지 않은 응답 데이터 또는 다중 선택 문항에 대한 시도
|
||||
* @throws NoSuchElementException 해당 ID의 답변이 없을 경우
|
||||
*/
|
||||
public Answer updateAnswer(UUID answerId, String answerText, UUID selectedOptionId) {
|
||||
Answer existingAnswer = getAnswerById(answerId); // 기존 답변 조회
|
||||
String questionType = questionService.getQuestionById(existingAnswer.getQuestionId()).getQuestionType();
|
||||
|
||||
// 다중 선택 문항은 이 메서드로 업데이트하지 않도록 제약
|
||||
if ("객관식_다중".equals(questionType)) {
|
||||
throw new IllegalArgumentException("Multi-select answers should be updated via specific methods (e.g., in AnswerSelectedOptionService).");
|
||||
}
|
||||
|
||||
Answer answer = Answer.builder()
|
||||
.answerId(answerId)
|
||||
.responseId(existingAnswer.getResponseId()) // 기존 응답 세션 ID 유지
|
||||
.questionId(existingAnswer.getQuestionId()) // 기존 문항 ID 유지
|
||||
.answerText(answerText)
|
||||
.selectedOptionId(selectedOptionId)
|
||||
.build();
|
||||
|
||||
answerMapper.updateAnswer(answer); // Mapper를 통해 답변 정보 업데이트
|
||||
return getAnswerById(answerId); // 업데이트된 최신 정보 반환
|
||||
}
|
||||
|
||||
/**
|
||||
* 특정 ID의 답변을 삭제합니다.
|
||||
* 객관식 다중 선택 문항의 경우, 관련 AnswerSelectedOption 레코드도 함께 삭제됩니다.
|
||||
*
|
||||
* @param answerId 삭제할 답변 고유 ID
|
||||
* @throws NoSuchElementException 해당 ID의 답변이 없을 경우
|
||||
*/
|
||||
public void deleteAnswer(UUID answerId) {
|
||||
getAnswerById(answerId); // 삭제할 답변이 존재하는지 먼저 확인합니다.
|
||||
|
||||
// 다중 선택 옵션이 있다면 먼저 삭제하여 외래 키 제약조건 위배를 방지합니다.
|
||||
answerSelectedOptionMapper.deleteByAnswerId(answerId);
|
||||
|
||||
answerMapper.deleteAnswer(answerId); // Mapper를 통해 답변 삭제
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
package sgis.surveysystem.service;
|
||||
|
||||
import sgis.surveysystem.domain.QuestionOption;
|
||||
import sgis.surveysystem.mapper.QuestionOptionMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Optional; // Optional 클래스 임포트
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 객관식 문항 선택지(QuestionOption) 관련 비즈니스 로직을 처리하는 서비스 클래스.
|
||||
* MyBatis Mapper를 통해 데이터베이스와 상호작용합니다.
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Transactional
|
||||
public class QuestionOptionService {
|
||||
|
||||
private final QuestionOptionMapper questionOptionMapper;
|
||||
private final QuestionService questionService; // 선택지가 속할 문항의 유효성 검사를 위해 QuestionService 주입
|
||||
|
||||
/**
|
||||
* 새로운 객관식 문항 선택지를 생성합니다.
|
||||
* 선택지 ID는 애플리케이션에서 UUID를 생성하여 할당합니다.
|
||||
*
|
||||
* @param questionId 선택지가 속할 문항 ID
|
||||
* @param optionText 선택지 내용
|
||||
* @param optionOrder 문항 내 선택지 순서
|
||||
* @return 생성된 QuestionOption 객체
|
||||
* @throws NoSuchElementException 해당 문항 ID가 유효하지 않을 경우
|
||||
*/
|
||||
public QuestionOption createQuestionOption(UUID questionId, String optionText, Integer optionOrder) {
|
||||
// 선택지가 속할 문항이 존재하는지 먼저 확인합니다.
|
||||
questionService.getQuestionById(questionId);
|
||||
// TODO: 해당 Question의 questionType이 '객관식_단일' 또는 '객관식_다중'인지 검증하는 로직 추가를 권장합니다.
|
||||
|
||||
QuestionOption option = QuestionOption.builder()
|
||||
.optionId(UUID.randomUUID()) // 애플리케이션에서 새로운 UUID 생성
|
||||
.questionId(questionId)
|
||||
.optionText(optionText)
|
||||
.optionOrder(optionOrder)
|
||||
.build();
|
||||
questionOptionMapper.insertQuestionOption(option); // Mapper를 통해 데이터베이스에 선택지 정보 삽입
|
||||
return option;
|
||||
}
|
||||
|
||||
/**
|
||||
* 특정 문항에 속한 모든 선택지를 순서대로 조회합니다.
|
||||
* 읽기 전용 트랜잭션으로 설정합니다.
|
||||
*
|
||||
* @param questionId 문항 고유 ID
|
||||
* @return 해당 문항의 모든 QuestionOption 객체 리스트
|
||||
* @throws NoSuchElementException 해당 문항 ID가 유효하지 않을 경우
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
public List<QuestionOption> getOptionsByQuestionId(UUID questionId) {
|
||||
questionService.getQuestionById(questionId); // 문항 존재 여부 확인
|
||||
return questionOptionMapper.findOptionsByQuestionId(questionId); // Mapper를 통해 문항 ID로 선택지 목록 조회
|
||||
}
|
||||
|
||||
/**
|
||||
* 특정 ID의 선택지를 조회합니다.
|
||||
* 읽기 전용 트랜잭션으로 설정합니다.
|
||||
*
|
||||
* @param optionId 선택지 고유 ID
|
||||
* @return 조회된 QuestionOption 객체
|
||||
* @throws NoSuchElementException 해당 ID의 선택지가 데이터베이스에 없을 경우 발생
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
public QuestionOption getQuestionOptionById(UUID optionId) {
|
||||
// Mapper에서 직접 QuestionOption 객체를 반환하도록 변경했으므로, Optional.ofNullable()을 사용합니다.
|
||||
return Optional.ofNullable(questionOptionMapper.findQuestionOptionById(optionId))
|
||||
.orElseThrow(() -> new NoSuchElementException("Question Option not found with ID: " + optionId)); // 없을 경우 예외 발생
|
||||
}
|
||||
|
||||
/**
|
||||
* 선택지 정보를 업데이트합니다.
|
||||
* 업데이트 전에 해당 선택지가 존재하는지 확인합니다.
|
||||
*
|
||||
* @param optionId 업데이트할 선택지의 고유 ID
|
||||
* @param optionText 새로운 선택지 내용
|
||||
* @param optionOrder 새로운 선택지 순서
|
||||
* @return 업데이트된 QuestionOption 객체 (업데이트 후 다시 조회하여 최신 상태 반환)
|
||||
* @throws NoSuchElementException 해당 ID의 선택지가 없을 경우
|
||||
*/
|
||||
public QuestionOption updateQuestionOption(UUID optionId, String optionText, Integer optionOrder) {
|
||||
getQuestionOptionById(optionId); // 업데이트할 선택지가 존재하는지 먼저 확인합니다.
|
||||
|
||||
QuestionOption option = QuestionOption.builder()
|
||||
.optionId(optionId) // 업데이트 대상 선택지 ID
|
||||
.optionText(optionText)
|
||||
.optionOrder(optionOrder)
|
||||
.build();
|
||||
questionOptionMapper.updateQuestionOption(option); // Mapper를 통해 선택지 정보 업데이트
|
||||
|
||||
return getQuestionOptionById(optionId); // 업데이트된 최신 정보를 다시 조회하여 반환합니다.
|
||||
}
|
||||
|
||||
/**
|
||||
* 특정 ID의 선택지를 삭제합니다.
|
||||
* 삭제 전에 해당 선택지가 존재하는지 확인합니다.
|
||||
*
|
||||
* @param optionId 삭제할 선택지의 고유 ID
|
||||
* @throws NoSuchElementException 해당 ID의 선택지가 없을 경우
|
||||
*/
|
||||
public void deleteQuestionOption(UUID optionId) {
|
||||
getQuestionOptionById(optionId); // 삭제할 선택지가 존재하는지 먼저 확인합니다.
|
||||
questionOptionMapper.deleteQuestionOption(optionId); // Mapper를 통해 선택지 삭제
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
package sgis.surveysystem.service;
|
||||
|
||||
import sgis.surveysystem.domain.Question;
|
||||
import sgis.surveysystem.mapper.QuestionMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Optional; // Optional 클래스 임포트
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 설문 문항(Question) 관련 비즈니스 로직을 처리하는 서비스 클래스.
|
||||
* MyBatis Mapper를 통해 데이터베이스와 상호작용합니다.
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Transactional
|
||||
public class QuestionService {
|
||||
|
||||
private final QuestionMapper questionMapper;
|
||||
private final SurveyService surveyService; // 문항이 속할 설문의 유효성 검사를 위해 SurveyService 주입
|
||||
|
||||
/**
|
||||
* 새로운 설문 문항을 생성합니다.
|
||||
* 문항 ID는 애플리케이션에서 UUID를 생성하여 할당합니다.
|
||||
*
|
||||
* @param surveyId 문항이 속할 설문 ID
|
||||
* @param questionText 문항 내용
|
||||
* @param questionType 문항 유형 ('단답형', '객관식_단일', '객관식_다중', '주관식')
|
||||
* @param questionOrder 설문 내 문항 순서
|
||||
* @param isRequired 필수 응답 여부
|
||||
* @return 생성된 Question 객체
|
||||
* @throws NoSuchElementException 해당 설문 ID가 유효하지 않을 경우
|
||||
*/
|
||||
public Question createQuestion(UUID surveyId, String questionText, String questionType, Integer questionOrder, Boolean isRequired) {
|
||||
// 문항이 속할 설문이 존재하는지 먼저 확인하여 외래 키 제약조건 위배를 방지합니다.
|
||||
surveyService.getSurveyById(surveyId);
|
||||
|
||||
Question question = Question.builder()
|
||||
.questionId(UUID.randomUUID()) // 애플리케이션에서 새로운 UUID 생성
|
||||
.surveyId(surveyId)
|
||||
.questionText(questionText)
|
||||
.questionType(questionType)
|
||||
.questionOrder(questionOrder)
|
||||
.isRequired(isRequired)
|
||||
.build();
|
||||
questionMapper.insertQuestion(question); // Mapper를 통해 데이터베이스에 문항 정보 삽입
|
||||
return question;
|
||||
}
|
||||
|
||||
/**
|
||||
* 특정 설문에 속한 모든 문항을 순서대로 조회합니다.
|
||||
* 읽기 전용 트랜잭션으로 설정합니다.
|
||||
*
|
||||
* @param surveyId 설문 고유 ID
|
||||
* @return 해당 설문의 모든 Question 객체 리스트
|
||||
* @throws NoSuchElementException 해당 설문 ID가 유효하지 않을 경우
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
public List<Question> getQuestionsBySurveyId(UUID surveyId) {
|
||||
surveyService.getSurveyById(surveyId); // 설문 존재 여부 확인
|
||||
return questionMapper.findQuestionsBySurveyId(surveyId); // Mapper를 통해 설문 ID로 문항 목록 조회
|
||||
}
|
||||
|
||||
/**
|
||||
* 특정 ID의 문항을 조회합니다.
|
||||
* 읽기 전용 트랜잭션으로 설정합니다.
|
||||
*
|
||||
* @param questionId 문항 고유 ID
|
||||
* @return 조회된 Question 객체
|
||||
* @throws NoSuchElementException 해당 ID의 문항이 데이터베이스에 없을 경우 발생
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
public Question getQuestionById(UUID questionId) {
|
||||
// Mapper에서 직접 Question 객체를 반환하도록 변경했으므로, Optional.ofNullable()을 사용합니다.
|
||||
return Optional.ofNullable(questionMapper.findQuestionById(questionId))
|
||||
.orElseThrow(() -> new NoSuchElementException("Question not found with ID: " + questionId)); // 없을 경우 예외 발생
|
||||
}
|
||||
|
||||
/**
|
||||
* 문항 정보를 업데이트합니다.
|
||||
* 업데이트 전에 해당 문항이 존재하는지 확인합니다.
|
||||
*
|
||||
* @param questionId 업데이트할 문항의 고유 ID
|
||||
* @param questionText 새로운 문항 내용
|
||||
* @param questionType 새로운 문항 유형
|
||||
* @param questionOrder 새로운 문항 순서
|
||||
* @param isRequired 새로운 필수 응답 여부
|
||||
* @return 업데이트된 Question 객체 (업데이트 후 다시 조회하여 최신 상태 반환)
|
||||
* @throws NoSuchElementException 해당 ID의 문항이 없을 경우
|
||||
*/
|
||||
public Question updateQuestion(UUID questionId, String questionText, String questionType, Integer questionOrder, Boolean isRequired) {
|
||||
getQuestionById(questionId); // 업데이트할 문항이 존재하는지 먼저 확인합니다.
|
||||
|
||||
Question question = Question.builder()
|
||||
.questionId(questionId) // 업데이트 대상 문항 ID
|
||||
.questionText(questionText)
|
||||
.questionType(questionType)
|
||||
.questionOrder(questionOrder)
|
||||
.isRequired(isRequired)
|
||||
.build();
|
||||
questionMapper.updateQuestion(question); // Mapper를 통해 문항 정보 업데이트
|
||||
|
||||
return getQuestionById(questionId); // 업데이트된 최신 정보를 다시 조회하여 반환합니다.
|
||||
}
|
||||
|
||||
/**
|
||||
* 특정 ID의 문항을 삭제합니다.
|
||||
* 삭제 전에 해당 문항이 존재하는지 확인합니다.
|
||||
*
|
||||
* @param questionId 삭제할 문항의 고유 ID
|
||||
* @throws NoSuchElementException 해당 ID의 문항이 없을 경우
|
||||
*/
|
||||
public void deleteQuestion(UUID questionId) {
|
||||
getQuestionById(questionId); // 삭제할 문항이 존재하는지 먼저 확인합니다.
|
||||
questionMapper.deleteQuestion(questionId); // Mapper를 통해 문항 삭제
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
package sgis.surveysystem.service;
|
||||
|
||||
import sgis.surveysystem.domain.Respondent;
|
||||
import sgis.surveysystem.mapper.RespondentMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 응답자(Respondent) 관련 비즈니스 로직을 처리하는 서비스 클래스.
|
||||
* MyBatis Mapper를 통해 데이터베이스와 상호작용합니다.
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Transactional
|
||||
public class RespondentService {
|
||||
|
||||
private final RespondentMapper respondentMapper;
|
||||
|
||||
/**
|
||||
* 새로운 응답자를 생성하거나, user_id가 주어지고 해당 user_id로 이미 존재하는 응답자를 조회합니다.
|
||||
*
|
||||
* @param userId 시스템 내 사용자 ID (선택 사항, 익명 응답 시 null)
|
||||
* @param ipAddress 응답자의 IP 주소 (선택 사항)
|
||||
* @return 생성되거나 조회된 Respondent 객체
|
||||
*/
|
||||
public Respondent createOrGetRespondent(String userId, String ipAddress) {
|
||||
// user_id가 제공된 경우, 기존 응답자가 있는지 먼저 확인합니다.
|
||||
if (userId != null && !userId.isEmpty()) {
|
||||
Optional<Respondent> existingRespondent = respondentMapper.findRespondentByUserId(userId);
|
||||
if (existingRespondent.isPresent()) {
|
||||
return existingRespondent.get(); // 이미 존재하는 응답자 반환
|
||||
}
|
||||
}
|
||||
|
||||
// 기존 응답자가 없거나 user_id가 제공되지 않은 경우, 새로운 응답자를 생성합니다.
|
||||
Respondent respondent = Respondent.builder()
|
||||
.respondentId(UUID.randomUUID()) // 애플리케이션에서 새로운 UUID 생성
|
||||
.userId(userId)
|
||||
.ipAddress(ipAddress)
|
||||
.responseStartAt(LocalDateTime.now()) // 응답 시작 시점 기록
|
||||
.build();
|
||||
respondentMapper.insertRespondent(respondent); // Mapper를 통해 응답자 정보 삽입
|
||||
return respondent;
|
||||
}
|
||||
|
||||
/**
|
||||
* 특정 ID의 응답자를 조회합니다.
|
||||
* 읽기 전용 트랜잭션으로 설정합니다.
|
||||
*
|
||||
* @param respondentId 응답자 고유 ID
|
||||
* @return 조회된 Respondent 객체
|
||||
* @throws NoSuchElementException 해당 ID의 응답자가 데이터베이스에 없을 경우 발생
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
public Respondent getRespondentById(UUID respondentId) {
|
||||
return respondentMapper.findRespondentById(respondentId) // Mapper를 통해 ID로 응답자 조회
|
||||
.orElseThrow(() -> new NoSuchElementException("Respondent not found with ID: " + respondentId)); // 없을 경우 예외 발생
|
||||
}
|
||||
|
||||
/**
|
||||
* 응답자 정보를 업데이트합니다.
|
||||
* 업데이트 전에 해당 응답자가 존재하는지 확인합니다.
|
||||
*
|
||||
* @param respondentId 업데이트할 응답자 고유 ID
|
||||
* @param userId 새로운 사용자 ID
|
||||
* @param ipAddress 새로운 IP 주소
|
||||
* @param responseEndAt 응답 완료 일시 (null이면 현재 시각으로 설정)
|
||||
* @return 업데이트된 Respondent 객체 (업데이트 후 다시 조회하여 최신 상태 반환)
|
||||
* @throws NoSuchElementException 해당 ID의 응답자가 없을 경우
|
||||
*/
|
||||
public Respondent updateRespondent(UUID respondentId, String userId, String ipAddress, LocalDateTime responseEndAt) {
|
||||
getRespondentById(respondentId); // 업데이트할 응답자가 존재하는지 먼저 확인합니다.
|
||||
|
||||
Respondent respondent = Respondent.builder()
|
||||
.respondentId(respondentId) // 업데이트 대상 응답자 ID
|
||||
.userId(userId)
|
||||
.ipAddress(ipAddress)
|
||||
.responseEndAt(responseEndAt != null ? responseEndAt : LocalDateTime.now()) // 완료 시점 갱신 또는 현재 시각
|
||||
.build();
|
||||
respondentMapper.updateRespondent(respondent); // Mapper를 통해 응답자 정보 업데이트
|
||||
return getRespondentById(respondentId); // 업데이트된 최신 정보를 다시 조회하여 반환합니다.
|
||||
}
|
||||
|
||||
/**
|
||||
* 특정 ID의 응답자를 삭제합니다.
|
||||
* 삭제 전에 해당 응답자가 존재하는지 확인합니다.
|
||||
*
|
||||
* @param respondentId 삭제할 응답자 고유 ID
|
||||
* @throws NoSuchElementException 해당 ID의 응답자가 없을 경우
|
||||
*/
|
||||
public void deleteRespondent(UUID respondentId) {
|
||||
getRespondentById(respondentId); // 삭제할 응답자가 존재하는지 먼저 확인합니다.
|
||||
respondentMapper.deleteRespondent(respondentId); // Mapper를 통해 응답자 삭제
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
package sgis.surveysystem.service;
|
||||
|
||||
import sgis.surveysystem.domain.SurveyResponse;
|
||||
import sgis.surveysystem.mapper.SurveyResponseMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 설문 응답 세션(SurveyResponse) 관련 비즈니스 로직을 처리하는 서비스 클래스.
|
||||
* 사용자가 설문을 제출할 때마다 생성되는 응답 세션을 관리합니다.
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Transactional
|
||||
public class SurveyResponseService {
|
||||
|
||||
private final SurveyResponseMapper surveyResponseMapper;
|
||||
private final SurveyService surveyService; // 외래 키 연결을 위해 SurveyService 주입
|
||||
private final RespondentService respondentService; // 외래 키 연결을 위해 RespondentService 주입
|
||||
|
||||
/**
|
||||
* 새로운 설문 응답 세션을 생성합니다.
|
||||
* 응답 세션 ID는 애플리케이션에서 UUID를 생성하여 할당합니다.
|
||||
*
|
||||
* @param surveyId 응답이 속한 설문 ID
|
||||
* @param respondentId 응답자 ID (선택 사항, 익명 설문인 경우 null)
|
||||
* @return 생성된 SurveyResponse 객체
|
||||
* @throws NoSuchElementException 설문 ID 또는 응답자 ID가 유효하지 않을 경우
|
||||
*/
|
||||
public SurveyResponse createSurveyResponse(UUID surveyId, UUID respondentId) {
|
||||
surveyService.getSurveyById(surveyId); // 설문 존재 여부 확인
|
||||
|
||||
// 응답자 ID가 제공된 경우, 해당 응답자가 존재하는지 확인합니다.
|
||||
if (respondentId != null) {
|
||||
respondentService.getRespondentById(respondentId);
|
||||
}
|
||||
|
||||
SurveyResponse surveyResponse = SurveyResponse.builder()
|
||||
.responseId(UUID.randomUUID()) // 애플리케이션에서 새로운 UUID 생성
|
||||
.surveyId(surveyId)
|
||||
.respondentId(respondentId)
|
||||
.submittedAt(LocalDateTime.now()) // 제출 시점 기록
|
||||
.build();
|
||||
surveyResponseMapper.insertSurveyResponse(surveyResponse); // Mapper를 통해 응답 세션 정보 삽입
|
||||
return surveyResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* 특정 ID의 설문 응답 세션을 조회합니다.
|
||||
* 읽기 전용 트랜잭션으로 설정합니다.
|
||||
*
|
||||
* @param responseId 응답 세션 고유 ID
|
||||
* @return 조회된 SurveyResponse 객체
|
||||
* @throws NoSuchElementException 해당 ID의 응답 세션이 데이터베이스에 없을 경우 발생
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
public SurveyResponse getSurveyResponseById(UUID responseId) {
|
||||
return surveyResponseMapper.findSurveyResponseById(responseId) // Mapper를 통해 ID로 응답 세션 조회
|
||||
.orElseThrow(() -> new NoSuchElementException("Survey Response not found with ID: " + responseId)); // 없을 경우 예외 발생
|
||||
}
|
||||
|
||||
/**
|
||||
* 특정 설문에 대한 모든 응답 세션을 조회합니다.
|
||||
* 읽기 전용 트랜잭션으로 설정합니다.
|
||||
*
|
||||
* @param surveyId 설문 고유 ID
|
||||
* @return 해당 설문의 모든 SurveyResponse 객체 리스트
|
||||
* @throws NoSuchElementException 해당 설문 ID가 유효하지 않을 경우
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
public List<SurveyResponse> getSurveyResponsesBySurveyId(UUID surveyId) {
|
||||
surveyService.getSurveyById(surveyId); // 설문 존재 여부 확인
|
||||
return surveyResponseMapper.findResponsesBySurveyId(surveyId); // Mapper를 통해 설문 ID로 응답 세션 목록 조회
|
||||
}
|
||||
|
||||
/**
|
||||
* 특정 응답자가 제출한 모든 응답 세션을 조회합니다.
|
||||
* 읽기 전용 트랜잭션으로 설정합니다.
|
||||
*
|
||||
* @param respondentId 응답자 고유 ID
|
||||
* @return 해당 응답자의 모든 SurveyResponse 객체 리스트
|
||||
* @throws NoSuchElementException 해당 응답자 ID가 유효하지 않을 경우
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
public List<SurveyResponse> getSurveyResponsesByRespondentId(UUID respondentId) {
|
||||
respondentService.getRespondentById(respondentId); // 응답자 존재 여부 확인
|
||||
return surveyResponseMapper.findResponsesByRespondentId(respondentId); // Mapper를 통해 응답자 ID로 응답 세션 목록 조회
|
||||
}
|
||||
|
||||
/**
|
||||
* 특정 ID의 설문 응답 세션을 삭제합니다.
|
||||
* 삭제 전에 해당 응답 세션이 존재하는지 확인합니다.
|
||||
*
|
||||
* @param responseId 삭제할 응답 세션 고유 ID
|
||||
* @throws NoSuchElementException 해당 ID의 응답 세션이 없을 경우
|
||||
*/
|
||||
public void deleteSurveyResponse(UUID responseId) {
|
||||
getSurveyResponseById(responseId); // 삭제할 응답 세션이 존재하는지 먼저 확인합니다.
|
||||
surveyResponseMapper.deleteSurveyResponse(responseId); // Mapper를 통해 응답 세션 삭제
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
package sgis.surveysystem.service;
|
||||
|
||||
import sgis.surveysystem.domain.Survey;
|
||||
import sgis.surveysystem.mapper.SurveyMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
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;
|
||||
|
||||
/**
|
||||
* 설문(Survey) 관련 비즈니스 로직을 처리하는 서비스 클래스.
|
||||
* MyBatis Mapper를 통해 데이터베이스와 상호작용합니다.
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor // Lombok을 사용하여 final 필드를 인자로 받는 생성자를 자동 생성합니다. (의존성 주입)
|
||||
@Transactional // 클래스 레벨에 트랜잭션 설정을 적용합니다.
|
||||
public class SurveyService {
|
||||
|
||||
private final SurveyMapper surveyMapper;
|
||||
|
||||
/**
|
||||
* 새로운 설문을 생성합니다.
|
||||
* 설문 ID는 애플리케이션에서 UUID를 생성하여 할당합니다.
|
||||
*
|
||||
* @param surveyTitle 설문 제목
|
||||
* @param surveyDescription 설문 상세 설명
|
||||
* @param startDate 설문 시작 일시
|
||||
* @param endDate 설문 종료 일시
|
||||
* @return 생성된 Survey 객체
|
||||
*/
|
||||
public Survey createSurvey(String surveyTitle, String surveyDescription, LocalDateTime startDate, LocalDateTime endDate) {
|
||||
// 애플리케이션에서 새로운 UUID를 생성하여 설문 ID로 사용합니다.
|
||||
// 데이터베이스의 DEFAULT gen_random_uuid() 설정과 무관하게 동작합니다.
|
||||
Survey survey = Survey.builder()
|
||||
.surveyId(UUID.randomUUID())
|
||||
.surveyTitle(surveyTitle)
|
||||
.surveyDescription(surveyDescription)
|
||||
.createdAt(LocalDateTime.now()) // 현재 시각으로 생성일 설정
|
||||
.updatedAt(LocalDateTime.now()) // 현재 시각으로 수정일 설정
|
||||
.isActive(true) // 기본적으로 활성화 상태로 설정
|
||||
.startDate(startDate)
|
||||
.endDate(endDate)
|
||||
.build();
|
||||
surveyMapper.insertSurvey(survey); // Mapper를 통해 데이터베이스에 설문 정보 삽입
|
||||
return survey;
|
||||
}
|
||||
|
||||
/**
|
||||
* 모든 설문 목록을 조회합니다.
|
||||
* 읽기 전용 트랜잭션으로 설정하여 성능을 최적화합니다.
|
||||
*
|
||||
* @return 모든 Survey 객체 리스트
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
public List<Survey> getAllSurveys() {
|
||||
return surveyMapper.findAllSurveys(); // Mapper를 통해 모든 설문 조회
|
||||
}
|
||||
|
||||
/**
|
||||
* 특정 ID의 설문을 조회합니다.
|
||||
* 읽기 전용 트랜잭션으로 설정합니다.
|
||||
*
|
||||
* @param surveyId 설문 고유 ID
|
||||
* @return 조회된 Survey 객체
|
||||
* @throws NoSuchElementException 해당 ID의 설문이 데이터베이스에 없을 경우 발생
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
public Survey getSurveyById(UUID surveyId) {
|
||||
// Mapper에서 직접 Survey 객체를 반환하도록 변경했으므로, Optional.ofNullable()을 사용합니다.
|
||||
return Optional.ofNullable(surveyMapper.findSurveyById(surveyId))
|
||||
.orElseThrow(() -> new NoSuchElementException("Survey not found with ID: " + surveyId)); // 없을 경우 예외 발생
|
||||
}
|
||||
|
||||
/**
|
||||
* 설문 정보를 업데이트합니다.
|
||||
* 업데이트 전에 해당 설문이 존재하는지 확인합니다.
|
||||
*
|
||||
* @param surveyId 업데이트할 설문의 고유 ID
|
||||
* @param surveyTitle 새로운 설문 제목
|
||||
* @param surveyDescription 새로운 설문 설명
|
||||
* @param isActive 설문 활성화 여부
|
||||
* @param startDate 새로운 설문 시작 일시
|
||||
* @param endDate 새로운 설문 종료 일시
|
||||
* @return 업데이트된 Survey 객체 (업데이트 후 다시 조회하여 최신 상태 반환)
|
||||
* @throws NoSuchElementException 해당 ID의 설문이 없을 경우
|
||||
*/
|
||||
public Survey updateSurvey(UUID surveyId, String surveyTitle, String surveyDescription, Boolean isActive, LocalDateTime startDate, LocalDateTime endDate) {
|
||||
// 업데이트할 설문이 존재하는지 먼저 확인합니다.
|
||||
getSurveyById(surveyId);
|
||||
|
||||
// 업데이트할 필드들을 포함하는 Survey 객체를 생성합니다.
|
||||
// MyBatis는 이 객체의 필드 값을 사용하여 SQL 쿼리의 파라미터를 채웁니다.
|
||||
Survey survey = Survey.builder()
|
||||
.surveyId(surveyId) // 업데이트 대상 설문 ID
|
||||
.surveyTitle(surveyTitle)
|
||||
.surveyDescription(surveyDescription)
|
||||
.isActive(isActive)
|
||||
.startDate(startDate)
|
||||
.endDate(endDate)
|
||||
.updatedAt(LocalDateTime.now()) // 업데이트 시각을 현재로 갱신
|
||||
.build();
|
||||
surveyMapper.updateSurvey(survey); // Mapper를 통해 설문 정보 업데이트
|
||||
|
||||
// 업데이트된 최신 정보를 데이터베이스에서 다시 조회하여 반환합니다.
|
||||
return getSurveyById(surveyId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 특정 ID의 설문을 삭제합니다.
|
||||
* 삭제 전에 해당 설문이 존재하는지 확인합니다.
|
||||
*
|
||||
* @param surveyId 삭제할 설문의 고유 ID
|
||||
* @throws NoSuchElementException 해당 ID의 설문이 없을 경우
|
||||
*/
|
||||
public void deleteSurvey(UUID surveyId) {
|
||||
// 삭제할 설문이 존재하는지 먼저 확인합니다.
|
||||
getSurveyById(surveyId);
|
||||
surveyMapper.deleteSurvey(surveyId); // Mapper를 통해 설문 삭제
|
||||
}
|
||||
|
||||
/**
|
||||
* 특정 ID의 설문을 비활성화합니다.
|
||||
* 비활성화 전에 해당 설문이 존재하는지 확인합니다.
|
||||
*
|
||||
* @param surveyId 비활성화할 설문의 고유 ID
|
||||
* @return 비활성화된 Survey 객체 (업데이트 후 다시 조회하여 최신 상태 반환)
|
||||
* @throws NoSuchElementException 해당 ID의 설문이 없을 경우
|
||||
*/
|
||||
public Survey deactivateSurvey(UUID surveyId) {
|
||||
getSurveyById(surveyId); // 존재 여부 확인
|
||||
surveyMapper.updateSurveyStatus(surveyId, false); // Mapper를 통해 설문 상태 업데이트
|
||||
return getSurveyById(surveyId); // 업데이트된 상태로 반환
|
||||
}
|
||||
}
|
||||
|
|
@ -141,6 +141,7 @@
|
|||
</if>
|
||||
<![CDATA[
|
||||
ORDER BY A.PROJECT_CODE desc
|
||||
LIMIT 1
|
||||
]]>
|
||||
</select>
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,76 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
|
||||
<mapper namespace="sgis.attach.mapper.AttachMapper">
|
||||
|
||||
<select id="selectAttachList" resultType="sgis.attach.entity.Attach">
|
||||
select attachment_id,
|
||||
post_id,
|
||||
file_name,
|
||||
stored_file_name,
|
||||
file_path,
|
||||
file_size,
|
||||
file_type,
|
||||
download_count,
|
||||
TO_CHAR(created_at,'YYYY-MM-DD') AS created_at
|
||||
from public.tb_attachment
|
||||
</select>
|
||||
|
||||
<insert id="insertAttach" parameterType="sgis.attach.entity.Attach">
|
||||
<selectKey keyProperty="idx" resultType="int" order="BEFORE">
|
||||
SELECT COALESCE(MAX(IDX) + 1, 1) FROM TB_ATTACHMENT
|
||||
</selectKey>
|
||||
insert into TB_ATTACHMENT(
|
||||
attachment_id
|
||||
,post_id
|
||||
,file_name
|
||||
,stored_file_name
|
||||
,file_path
|
||||
,file_size
|
||||
,file_type
|
||||
,download_count
|
||||
,created_at
|
||||
) values(
|
||||
#{attachment_id}
|
||||
,#{post_id}
|
||||
,#{file_name}
|
||||
,#{stored_file_name}
|
||||
,#{file_path}
|
||||
,#{file_size}
|
||||
,#{file_type}
|
||||
,0
|
||||
,NOW()
|
||||
)
|
||||
</insert>
|
||||
|
||||
<insert id="insertAttachList" parameterType="list">
|
||||
<selectKey keyProperty="idx" resultType="int" order="BEFORE">
|
||||
SELECT COALESCE(MAX(IDX) + 1, 1) FROM TB_ATTACHMENT
|
||||
</selectKey>
|
||||
insert into TB_ATTACHMENT(
|
||||
attachment_id
|
||||
,post_id
|
||||
,file_name
|
||||
,stored_file_name
|
||||
,file_path
|
||||
,file_size
|
||||
,file_type
|
||||
,download_count
|
||||
,created_at
|
||||
) values
|
||||
<foreach item="item" collection="list" separator=",">
|
||||
(
|
||||
#{attachment_id}
|
||||
,#{post_id}
|
||||
,#{file_name}
|
||||
,#{stored_file_name}
|
||||
,#{file_path}
|
||||
,#{file_size}
|
||||
,#{file_type}
|
||||
,0
|
||||
,NOW()
|
||||
)
|
||||
</foreach>
|
||||
|
||||
</insert>
|
||||
|
||||
</mapper>
|
||||
|
|
@ -54,4 +54,514 @@
|
|||
ORDER BY HOL_COD
|
||||
]]>
|
||||
</select>
|
||||
|
||||
|
||||
<select id="selectHoleCount" parameterType="map" resultType="egovMap">
|
||||
<![CDATA[
|
||||
SELECT
|
||||
COUNT(HOLE_CODE) AS CNT
|
||||
FROM TBL_HEADER
|
||||
]]>
|
||||
<choose>
|
||||
<when test="areaHcodeArray != null and areaHcodeArray != '' and areaHcodeArray != 'undefiend'">
|
||||
<![CDATA[
|
||||
where NVL(USE_YN,' ') <> 'N' AND HOLE_CODE IN
|
||||
]]>
|
||||
<foreach collection="areaHcodeArray" item="hole" index="index" open="(" close=")" separator=",">
|
||||
#{hole}
|
||||
</foreach>
|
||||
</when>
|
||||
<otherwise>
|
||||
<![CDATA[
|
||||
WHERE NVL(USE_YN,' ') <> 'N' AND HOLE_LOCATION_TM_X >= #{minX} AND HOLE_LOCATION_TM_X <= #{maxX}
|
||||
AND HOLE_LOCATION_TM_Y >= #{minY} AND HOLE_LOCATION_TM_Y <= #{maxY}
|
||||
]]>
|
||||
</otherwise>
|
||||
</choose>
|
||||
</select>
|
||||
|
||||
<select id="selectHoleList" parameterType="map" resultType="egovMap">
|
||||
<![CDATA[
|
||||
SELECT
|
||||
HOLE_CODE
|
||||
FROM TBL_HEADER
|
||||
]]>
|
||||
<choose>
|
||||
<when test="areaHcodeArray != null and areaHcodeArray != ''and areaHcodeArray != 'undefiend'">
|
||||
<![CDATA[
|
||||
where NVL(USE_YN,' ') <> 'N' AND HOLE_CODE IN
|
||||
]]>
|
||||
<foreach collection="areaHcodeArray" item="hole" index="index" open="(" close=")" separator=",">
|
||||
#{hole}
|
||||
</foreach>
|
||||
</when>
|
||||
<otherwise>
|
||||
<![CDATA[
|
||||
WHERE NVL(USE_YN,' ') <> 'N' AND HOLE_LOCATION_TM_X >= #{minX} AND HOLE_LOCATION_TM_X <= #{maxX}
|
||||
AND HOLE_LOCATION_TM_Y >= #{minY} AND HOLE_LOCATION_TM_Y <= #{maxY}
|
||||
]]>
|
||||
</otherwise>
|
||||
</choose>
|
||||
|
||||
</select>
|
||||
|
||||
<select id="selectItems" parameterType="map" resultType="egovMap">
|
||||
SELECT
|
||||
HOLE_CODE,B.PROJECT_NAME,
|
||||
(SELECT USCS_NAME FROM TBL_SCIENCE_LAYER_TB WHERE A.LAYER_CLASS_CODE = USCS_CODE) LAYER,
|
||||
A.HOLE_TOTAL_DEPTH DEPTH,A.HOLE_EL EL,A.HOLE_WL WL, A.HOLE_DATE_FROM SDATE, A.HOLE_DATE_TO EDATE
|
||||
FROM
|
||||
TBL_HEADER A, TBL_PROJECT_INFO B
|
||||
WHERE
|
||||
<![CDATA[
|
||||
NVL(A.USE_YN,' ') <> 'N' AND NVL(B.USE_YN,' ') <> 'N' AND
|
||||
]]>
|
||||
A.PROJECT_CODE = B.PROJECT_CODE
|
||||
AND A.HOLE_CODE IN
|
||||
<foreach collection="holeCode" item="hole" index="index" open="(" close=")" separator=",">
|
||||
#{hole}
|
||||
</foreach>
|
||||
ORDER BY
|
||||
HOLE_CODE
|
||||
</select>
|
||||
|
||||
<select id="selectDistrict" parameterType="map" resultType="egovMap">
|
||||
SELECT
|
||||
'L_DIS' CODE, LD_ID CODE_VALUE, LD_DISTRICTCODE CODE_TEXT, 'L_DIS' PARENT_CODE, LD_ID PARENT_CODE_VALUE
|
||||
FROM TBL_L_DISTRICT
|
||||
</select>
|
||||
|
||||
<select id="selectDistrictSgg" parameterType="map" resultType="egovMap">
|
||||
SELECT
|
||||
'M_DIS' CODE, MD_ID CODE_VALUE, MD_DISTRICTCODE CODE_TEXT, 'L_DIS' PARENT_CODE, MD_LARGEDISTRICTID RARENT_CODE
|
||||
FROM TBL_M_DISTRICT
|
||||
WHERE MD_LARGEDISTRICTID = #{code}
|
||||
ORDER BY CODE_TEXT
|
||||
</select>
|
||||
|
||||
<select id="selectGrid1" parameterType="map" resultType="EgovMap">
|
||||
SELECT SA.*
|
||||
, (SELECT CRWD_ID FROM S3D_CROWD_ANAL OA WHERE OA.PROJECT_CODE = SA.PROJECT_CODE AND ROWNUM=1) AS CRWD_ID
|
||||
, (SELECT SEPARATOR FROM S3D_CROWD_ANAL OA WHERE OA.PROJECT_CODE = SA.PROJECT_CODE AND ROWNUM=1) AS SEPARATOR
|
||||
, 'fault_model' as FAULT_MODEL
|
||||
, 'spt_model' as SPT_MODEL
|
||||
, 'analysis_model' as ANALYSIS_MODEL
|
||||
, 'layer_model' as LAYER_MODEL
|
||||
, 'gw_model' as GW_MODEL
|
||||
, 'no' as CAD
|
||||
, 'no' as SHAPE
|
||||
FROM (
|
||||
SELECT
|
||||
A.PROJECT_CODE
|
||||
, replace(replace(A.PROJECT_NAME ,chr(10),' '),chr(13),' ') PROJECT_NAME
|
||||
, COUNT(B.HOLE_CODE) HOLE_CNT
|
||||
FROM
|
||||
TBL_PROJECT_INFO A
|
||||
, TBL_HEADER B
|
||||
WHERE
|
||||
<![CDATA[
|
||||
NVL(A.USE_YN,' ') <> 'N' AND NVL(B.USE_YN,' ') <> 'N' AND
|
||||
]]>
|
||||
A.PROJECT_CODE = B.PROJECT_CODE
|
||||
<choose>
|
||||
<when test="sido != '' and sido != null">
|
||||
<![CDATA[
|
||||
AND SUBSTR(A.PROJECT_START_SPOT, 1, 3) = #{sido}
|
||||
]]>
|
||||
</when>
|
||||
<when test="gugun != '' and gugun != null">
|
||||
<![CDATA[
|
||||
AND SUBSTR(A.PROJECT_START_SPOT, 1, 6) = #{sido+gugun}
|
||||
]]>
|
||||
</when>
|
||||
</choose>
|
||||
GROUP
|
||||
BY A.PROJECT_CODE, A.PROJECT_NAME
|
||||
) SA
|
||||
WHERE EXISTS (SELECT 1
|
||||
FROM S3D_CROWD_ANAL S2A
|
||||
WHERE S2A.PROJECT_CODE = SA.PROJECT_CODE)
|
||||
</select>
|
||||
|
||||
<select id="selectOldItems" parameterType="map" resultType="EgovMap">
|
||||
SELECT
|
||||
C.S3D_DISTRICT_CODE,
|
||||
C.S3D_DISTRICT_NAME,
|
||||
C.S3D_SEQUENCE,
|
||||
C.S3D_PREFIX,
|
||||
B.S3D_DISTRICT_SUB_CODE,
|
||||
B.S3D_DISTRICT_SUB_NAME,
|
||||
A.S3D_REGION_NAME,
|
||||
A.S3D_CODE,
|
||||
A.S3D_MAP_IMAGE
|
||||
FROM
|
||||
S3D_MASTER A,
|
||||
S3D_DISTRICT_SUB B,
|
||||
S3D_DISTRICT C
|
||||
WHERE
|
||||
A.S3D_DISTRICT_SUB = B.S3D_DISTRICT_SUB_CODE
|
||||
AND A.S3D_DISTRICT = C.S3D_DISTRICT_CODE
|
||||
|
||||
<if test="sido != null and sido != '' and sido != 'all'">
|
||||
<![CDATA[
|
||||
AND C.S3D_DISTRICT_CODE = #{sido}
|
||||
]]>
|
||||
</if>
|
||||
<if test="gugun != null and gugun != '' and gugun != 'all'">
|
||||
<![CDATA[
|
||||
AND B.S3D_DISTRICT_SUB_CODE = #{gugun}
|
||||
]]>
|
||||
</if>
|
||||
<if test="dong != null and dong != '' and dong != 'all'">
|
||||
<![CDATA[
|
||||
AND A.S3D_MAP_IMAGE LIKE #{dong} || '%'
|
||||
]]>
|
||||
</if>
|
||||
ORDER BY
|
||||
C.S3D_DISTRICT_CODE, C.S3D_SEQUENCE, B.S3D_DISTRICT_SUB_CODE, A.S3D_CODE
|
||||
</select>
|
||||
|
||||
<select id="get3dSido" parameterType="map" resultType="egovMap">
|
||||
select
|
||||
s3d_district_code, s3d_district_name, s3d_sequence, s3d_prefix
|
||||
from s3d_district
|
||||
where
|
||||
s3d_district_yn = '1'
|
||||
</select>
|
||||
|
||||
<select id="get3dGugun" parameterType="map" resultType="egovMap">
|
||||
select s3d_district, s3d_district_sub_name, s3d_district_sub_code from s3d_district_sub
|
||||
where s3d_district = #{doCd}
|
||||
</select>
|
||||
|
||||
<select id="get3dDong" parameterType="map" resultType="egovMap">
|
||||
select
|
||||
s3d_region, s3d_link_code, s3d_button_name, s3d_link_address, s3d_link_x, s3d_link_y
|
||||
from
|
||||
s3d_detail
|
||||
where
|
||||
s3d_district = #{sido} and
|
||||
s3d_district_sub = #{gugun}
|
||||
order by
|
||||
s3d_link_code asc
|
||||
</select>
|
||||
|
||||
<select id="get3dDetail" parameterType="map" resultType="egovMap">
|
||||
select
|
||||
DISTINCT de.s3d_region, de.s3d_link_code, ma.s3d_district, de.s3d_button_name
|
||||
from
|
||||
s3d_detail de, s3d_master ma
|
||||
where
|
||||
de.s3d_district = ma.s3d_district and
|
||||
ma.s3d_map_image = #{adong1} and
|
||||
de.s3d_region= #{aid}
|
||||
order by
|
||||
de.s3d_link_code asc
|
||||
</select>
|
||||
|
||||
<select id="getGeneralData" parameterType="map" resultType="egovMap">
|
||||
SELECT
|
||||
(
|
||||
COALESCE(t_project.project_code, '')
|
||||
|| '|' || COALESCE(t_hole.hole_elevation, '') -- HOLE_ELV -> hole_elevation
|
||||
|| '|' || COALESCE(t_hole.hole_drilling_depth, '') -- 시추심도
|
||||
|| '|' || COALESCE(t_hole.hole_x, '')
|
||||
|| '|' || COALESCE(t_hole.hole_y, '')
|
||||
|| '|' || COALESCE(ST_Y(t_hole.hole_point)::text, '')
|
||||
|| '|' || COALESCE(ST_X(t_hole.hole_point)::text, '')
|
||||
|| '|' || COALESCE(t_business.business_name, '')
|
||||
|| '|' || COALESCE(t_hole.hole_water_level, '') -- HOLE_WL -> hole_water_level
|
||||
|| '|' || COALESCE(t_hole.hole_location_sd, '') || COALESCE(t_hole.hole_location_sgg, '') || COALESCE(t_hole.hole_location_emd, '') || COALESCE(t_hole.hole_location_detail, '')
|
||||
) AS VLU
|
||||
FROM
|
||||
public.apptb_hole01 t_hole
|
||||
LEFT JOIN
|
||||
public.apptb_saup01 t_business ON t_hole.business_code = t_business.business_code
|
||||
LEFT JOIN
|
||||
public.apptb_proj01 t_project ON t_business.project_code = t_project.project_code
|
||||
WHERE
|
||||
t_hole.business_code = #{businessCode}
|
||||
AND t_hole.hole_code = #{holeCode}
|
||||
</select>
|
||||
|
||||
<select id="getLinkLayerData" parameterType="map" resultType="egovMap">
|
||||
SELECT
|
||||
a1.layer_depth_from AS "FROM",
|
||||
a1.layer_depth_to AS "TO",
|
||||
a1.layer_science_name AS "USCS",
|
||||
COALESCE(a1.layer_eng_name, '') AS "NAME",
|
||||
COALESCE(a1.layer_soil_color, '') AS "COLOR",
|
||||
REPLACE(REPLACE(a1.layer_desc, CHR(10), ' '), CHR(13), ' ') AS "DESC"
|
||||
FROM
|
||||
public.apptb_layr01 a1
|
||||
WHERE
|
||||
a1.business_code = #{businessCode}
|
||||
AND a1.hole_code = #{holeCode}
|
||||
ORDER BY a1.layer_depth_from
|
||||
</select>
|
||||
|
||||
<select id="getSPTData" parameterType="map" resultType="egovMap">
|
||||
SELECT
|
||||
depth_spt,
|
||||
spt_n
|
||||
FROM
|
||||
public.apptb_spt01
|
||||
WHERE
|
||||
hole_code = #{holeCode}
|
||||
AND
|
||||
business_code = #{businessCode}
|
||||
ORDER BY
|
||||
depth_spt
|
||||
</select>
|
||||
|
||||
<select id="getSampleData" parameterType="map" resultType="egovMap">
|
||||
<![CDATA[
|
||||
SELECT
|
||||
(t.CODE || '|' || t.DEPTH_FROM || '|' || t.DEPTH_TO || '|' || t.YN || '|' || t.SHAPE) AS VAL
|
||||
FROM
|
||||
(
|
||||
SELECT
|
||||
'0' AS CODE,
|
||||
t1.rjoint_depth_from::numeric AS DEPTH_FROM,
|
||||
t1.rjoint_depth_to::numeric AS DEPTH_TO,
|
||||
'0' AS YN,
|
||||
'0' AS SHAPE
|
||||
FROM
|
||||
public.apptb_josh01 t1
|
||||
WHERE
|
||||
t1.hole_code = #{holeCode}
|
||||
AND t1.business_code = #{businessCode}
|
||||
UNION ALL
|
||||
SELECT
|
||||
'0' AS CODE,
|
||||
t2.depth_from::numeric AS DEPTH_FROM,
|
||||
t2.depth_to::numeric AS DEPTH_TO,
|
||||
'0' AS YN,
|
||||
'0' AS SHAPE
|
||||
FROM
|
||||
public.apptb_polo01 t2
|
||||
WHERE
|
||||
t2.hole_code = #{holeCode}
|
||||
AND t2.business_code = #{businessCode}
|
||||
UNION ALL
|
||||
SELECT
|
||||
'0' AS CODE,
|
||||
t3.rtri_depth_from::numeric AS DEPTH_FROM,
|
||||
t3.rtri_depth_to::numeric AS DEPTH_TO,
|
||||
'0' AS YN,
|
||||
'0' AS SHAPE
|
||||
FROM
|
||||
public.apptb_rtri01 t3
|
||||
WHERE
|
||||
t3.hole_code = #{holeCode}
|
||||
AND t3.business_code = #{businessCode}
|
||||
UNION ALL
|
||||
SELECT
|
||||
'0' AS CODE,
|
||||
t4.runi_depth_from::numeric AS DEPTH_FROM,
|
||||
t4.runi_depth_to::numeric AS DEPTH_TO,
|
||||
'0' AS YN,
|
||||
'0' AS SHAPE
|
||||
FROM
|
||||
public.apptb_runi01 t4
|
||||
WHERE
|
||||
t4.hole_code = #{holeCode}
|
||||
AND t4.business_code = #{businessCode}
|
||||
UNION ALL
|
||||
SELECT
|
||||
t5.sample_code AS CODE,
|
||||
t5.sample_depth_from::numeric AS DEPTH_FROM,
|
||||
t5.sample_depth_to::numeric AS DEPTH_TO,
|
||||
'0' AS YN,
|
||||
t5.sample_shape AS SHAPE
|
||||
FROM
|
||||
public.apptb_smpl01 t5
|
||||
WHERE
|
||||
t5.hole_code = #{holeCode}
|
||||
AND t5.business_code = #{businessCode}
|
||||
AND (
|
||||
(SELECT COUNT(*) FROM public.apptb_cons02 WHERE sample_code = t5.sample_code) > 0 OR
|
||||
(SELECT COUNT(*) FROM public.apptb_stri01 WHERE sample_code = t5.sample_code) > 0 OR
|
||||
(SELECT COUNT(*) FROM public.apptb_suni01 WHERE sample_code = t5.sample_code) > 0 OR
|
||||
(SELECT COUNT(*) FROM public.apptb_clas01 WHERE sample_code = t5.sample_code) > 0 OR
|
||||
(SELECT COUNT(*) FROM public.apptb_cbr01 WHERE sample_code = t5.sample_code) > 0
|
||||
)
|
||||
) AS t
|
||||
WHERE
|
||||
t.DEPTH_FROM IS NOT NULL
|
||||
AND t.DEPTH_TO IS NOT NULL
|
||||
]]>
|
||||
</select>
|
||||
<select id="selectInfoLastPage" parameterType="map" resultType="egovMap">
|
||||
<![CDATA[
|
||||
SELECT CEIL(MAX(ROWNUM)/ 15 ) AS LASTPAGE
|
||||
FROM (
|
||||
SELECT HOLE_CODE,
|
||||
B.PROJECT_NAME,
|
||||
(SELECT USCS_NAME
|
||||
FROM TBL_SCIENCE_LAYER_TB
|
||||
WHERE A.LAYER_CLASS_CODE = USCS_CODE) LAYER,
|
||||
A.HOLE_TOTAL_DEPTH DEPTH,
|
||||
A.HOLE_EL EL,
|
||||
A.HOLE_WL WL,
|
||||
A.HOLE_DATE_FROM SDATE,
|
||||
A.HOLE_DATE_TO EDATE
|
||||
FROM
|
||||
TBL_HEADER A,
|
||||
TBL_PROJECT_INFO B
|
||||
WHERE
|
||||
NVL(A.USE_YN,' ') <> 'N' AND NVL(B.USE_YN,' ') <> 'N' AND
|
||||
A.PROJECT_CODE = B.PROJECT_CODE
|
||||
AND A.HOLE_CODE IN
|
||||
|
||||
]]>
|
||||
<choose>
|
||||
<when test="areaHcodeArray != null and areaHcodeArray != ''">
|
||||
<foreach collection="areaHcodeArray" item="hole" index="index" open="(" close=")" separator=",">
|
||||
#{hole}
|
||||
</foreach>
|
||||
</when>
|
||||
<otherwise>
|
||||
<![CDATA[
|
||||
(
|
||||
SELECT TRIM(HOLE_CODE)
|
||||
FROM TBL_HEADER A
|
||||
WHERE NVL(A.USE_YN,' ') <> 'N' AND HOLE_LOCATION_TM_X >= #{minX} AND HOLE_LOCATION_TM_X <= #{maxX}
|
||||
AND HOLE_LOCATION_TM_Y >= #{minY} AND HOLE_LOCATION_TM_Y <= #{maxY}
|
||||
)
|
||||
]]>
|
||||
</otherwise>
|
||||
</choose>
|
||||
<![CDATA[
|
||||
|
||||
ORDER BY HOLE_CODE )
|
||||
]]>
|
||||
</select>
|
||||
|
||||
<select id="selectInfoItems" parameterType="map" resultType="egovMap">
|
||||
<![CDATA[
|
||||
SELECT *
|
||||
FROM (SELECT ROWNUM RNK,
|
||||
Z.*
|
||||
FROM (SELECT HOLE_CODE,
|
||||
B.PROJECT_NAME,
|
||||
B.PROJECT_CODE,
|
||||
(SELECT USCS_NAME
|
||||
FROM TBL_SCIENCE_LAYER_TB
|
||||
WHERE A.LAYER_CLASS_CODE = USCS_CODE) LAYER,
|
||||
A.HOLE_TOTAL_DEPTH DEPTH,
|
||||
A.HOLE_EL EL,
|
||||
A.HOLE_WL WL,
|
||||
A.HOLE_DATE_FROM SDATE,
|
||||
A.HOLE_DATE_TO EDATE
|
||||
FROM TBL_HEADER A,
|
||||
TBL_PROJECT_INFO B
|
||||
WHERE
|
||||
NVL(A.USE_YN,' ') <> 'N' AND NVL(B.USE_YN,' ') <> 'N' AND
|
||||
A.PROJECT_CODE = B.PROJECT_CODE
|
||||
AND A.HOLE_CODE IN
|
||||
]]>
|
||||
<choose>
|
||||
<when test="areaHcodeArray != null and areaHcodeArray != ''">
|
||||
<foreach collection="areaHcodeArray" item="hole" index="index" open="(" close=")" separator=",">
|
||||
#{hole}
|
||||
</foreach>
|
||||
</when>
|
||||
<otherwise>
|
||||
<![CDATA[
|
||||
(
|
||||
SELECT TRIM(HOLE_CODE)
|
||||
FROM
|
||||
TBL_HEADER A
|
||||
WHERE
|
||||
NVL(USE_YN,' ') <> 'N' AND HOLE_LOCATION_TM_X >= #{minX} AND HOLE_LOCATION_TM_X <= #{maxX}
|
||||
AND HOLE_LOCATION_TM_Y >= #{minY} AND HOLE_LOCATION_TM_Y <= #{maxY}
|
||||
)
|
||||
]]>
|
||||
</otherwise>
|
||||
</choose>
|
||||
|
||||
|
||||
<![CDATA[
|
||||
ORDER BY HOLE_CODE ) Z )
|
||||
WHERE RNK > #{firstRow} AND RNK <= #{lastRow}
|
||||
]]>
|
||||
</select>
|
||||
|
||||
<select id="selectWebDownloadLog" parameterType="map" resultType="egovMap">
|
||||
<![CDATA[
|
||||
SELECT
|
||||
(sysdate-min(ENDDATE)) as ENDDATE,
|
||||
COUNT(*) AS CNT
|
||||
FROM WEB_DOWNLOAD_LOG
|
||||
WHERE USERID = #{userId}
|
||||
AND DOWNYN IN ('W','N','R')
|
||||
]]>
|
||||
</select>
|
||||
|
||||
<select id="selectProjectList" parameterType="map" resultType="egovMap">
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
TBL_PROJECT_INFO
|
||||
WHERE
|
||||
<![CDATA[
|
||||
NVL(USE_YN,' ') <> 'N' AND
|
||||
]]>
|
||||
TRIM(PROJECT_CODE) IN
|
||||
<foreach collection="projectList" item="project" index="index" open="(" close=")" separator=",">
|
||||
#{project}
|
||||
</foreach>
|
||||
|
||||
</select>
|
||||
|
||||
<select id="mapBoreholeLogHeader" parameterType="map" resultType="egovMap">
|
||||
SELECT
|
||||
ap.project_code,
|
||||
hole.hole_code AS HOLE_CODE,
|
||||
ap.project_name,
|
||||
cc1.code_txt ||
|
||||
CASE
|
||||
WHEN cc2.code_txt IS NOT NULL THEN ' ' || cc2.code_txt
|
||||
ELSE ''
|
||||
END AS COMPANY,
|
||||
hole.hole_name AS HOLE_NAME,
|
||||
cc3.code_txt AS DISTRICT,
|
||||
hole.hole_x AS HOLE_OR_X,
|
||||
hole.hole_y AS HOLE_OR_Y,
|
||||
hole.HOLE_ELEVATION AS HOLE_EL,
|
||||
hole.HOLE_END_DATE AS HOLE_DATE_TO,
|
||||
hole.HOLE_DRILLING_DEPTH AS HOLE_TOTAL_DEPTH,
|
||||
hole.HOLE_CASING_DEPTH AS HOLE_CASING_DEPTH,
|
||||
hole.HOLE_WATER_LEVEL AS hole_wl,
|
||||
hole.HOLE_BORING_MACHINE AS HOLE_BORING_MACHINE,
|
||||
hole.HOLE_BORING_METHOD AS HOLE_BORING_METHOD,
|
||||
hole.HOLE_BORING_NAME AS HOLE_BORING_BY,
|
||||
'N/A' AS HOLE_INSPECTED_BY,
|
||||
REPLACE((epsg_code.code_txt || ' ' || epsg_code2.code_txt), '(BESSEL)','') AS COORDINATE_L,
|
||||
(
|
||||
SELECT
|
||||
CASE
|
||||
WHEN MAX(layer.LAYER_DEPTH_TO::NUMERIC) >= 34.6 THEN 3
|
||||
WHEN MAX(layer.LAYER_DEPTH_TO::NUMERIC) >= 17.3 THEN 2
|
||||
ELSE 1
|
||||
END
|
||||
FROM APPTB_LAYR01 layer
|
||||
WHERE layer.business_code = hole.business_code -- 현재 행의 business_code 사용
|
||||
AND layer.hole_code = hole.hole_code -- 현재 행의 hole_code 사용
|
||||
) AS PAGE_NUM
|
||||
FROM APPTB_HOLE01 hole
|
||||
LEFT JOIN APPTB_SAUP01 business ON hole.business_code = business.business_code
|
||||
LEFT JOIN APPTB_PROJ01 ap ON business.project_code = ap.project_code
|
||||
LEFT JOIN comtb_code02 cc1 ON ap.project_ordering_code = cc1.code_val
|
||||
LEFT JOIN comtb_code02 cc2 ON ap.project_affiliated_code = cc2.code_val
|
||||
LEFT JOIN comtb_code02 cc3 ON ap.project_harbor_code = cc3.code_val
|
||||
LEFT JOIN comtb_code02 epsg_code ON ap.PROJECT_EPSG_CODE = epsg_code.code_val
|
||||
LEFT JOIN comtb_code02 epsg_code2 ON hole.hole_coordinate = epsg_code2.code_val
|
||||
WHERE
|
||||
hole.business_code = #{businessCode} AND
|
||||
hole.hole_code = #{holeCode}
|
||||
</select>
|
||||
|
||||
|
||||
</mapper>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,74 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="sgis.surveysystem.mapper.AnswerMapper">
|
||||
|
||||
<resultMap id="answerResultMap" type="sgis.surveysystem.domain.Answer">
|
||||
<id property="answerId" column="answer_id"/>
|
||||
<result property="responseId" column="response_id"/>
|
||||
<result property="questionId" column="question_id"/>
|
||||
<result property="answerText" column="answer_text"/>
|
||||
<result property="selectedOptionId" column="selected_option_id"/>
|
||||
</resultMap>
|
||||
|
||||
<insert id="insertAnswer" parameterType="sgis.surveysystem.domain.Answer">
|
||||
INSERT INTO tb_answers (
|
||||
answer_id,
|
||||
response_id,
|
||||
question_id,
|
||||
answer_text,
|
||||
selected_option_id
|
||||
) VALUES (
|
||||
#{answerId, jdbcType=OTHER},
|
||||
#{responseId, jdbcType=OTHER},
|
||||
#{questionId, jdbcType=OTHER},
|
||||
#{answerText},
|
||||
#{selectedOptionId, jdbcType=OTHER}
|
||||
)
|
||||
</insert>
|
||||
|
||||
<select id="findAnswerById" resultMap="answerResultMap">
|
||||
SELECT
|
||||
answer_id,
|
||||
response_id,
|
||||
question_id,
|
||||
answer_text,
|
||||
selected_option_id
|
||||
FROM tb_answers
|
||||
WHERE answer_id = #{answerId, jdbcType=OTHER}
|
||||
</select>
|
||||
|
||||
<select id="findAnswersByResponseId" resultMap="answerResultMap">
|
||||
SELECT
|
||||
answer_id,
|
||||
response_id,
|
||||
question_id,
|
||||
answer_text,
|
||||
selected_option_id
|
||||
FROM tb_answers
|
||||
WHERE response_id = #{responseId, jdbcType=OTHER}
|
||||
</select>
|
||||
|
||||
<select id="findAnswersByQuestionId" resultMap="answerResultMap">
|
||||
SELECT
|
||||
answer_id,
|
||||
response_id,
|
||||
question_id,
|
||||
answer_text,
|
||||
selected_option_id
|
||||
FROM tb_answers
|
||||
WHERE question_id = #{questionId, jdbcType=OTHER}
|
||||
</select>
|
||||
|
||||
<update id="updateAnswer" parameterType="sgis.surveysystem.domain.Answer">
|
||||
UPDATE tb_answers
|
||||
SET
|
||||
answer_text = #{answerText},
|
||||
selected_option_id = #{selectedOptionId, jdbcType=OTHER}
|
||||
WHERE answer_id = #{answerId, jdbcType=OTHER}
|
||||
</update>
|
||||
|
||||
<delete id="deleteAnswer">
|
||||
DELETE FROM tb_answers
|
||||
WHERE answer_id = #{answerId, jdbcType=OTHER}
|
||||
</delete>
|
||||
</mapper>
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="sgis.surveysystem.mapper.AnswerSelectedOptionMapper">
|
||||
|
||||
<resultMap id="answerSelectedOptionResultMap" type="sgis.surveysystem.domain.AnswerSelectedOption">
|
||||
<id property="answerId" column="answer_id"/>
|
||||
<id property="optionId" column="option_id"/>
|
||||
</resultMap>
|
||||
|
||||
<insert id="insertAnswerSelectedOption" parameterType="sgis.surveysystem.domain.AnswerSelectedOption">
|
||||
INSERT INTO tb_answer_selected_options (
|
||||
answer_id,
|
||||
option_id
|
||||
) VALUES (
|
||||
#{answerId, jdbcType=OTHER},
|
||||
#{optionId, jdbcType=OTHER}
|
||||
)
|
||||
</insert>
|
||||
|
||||
<insert id="insertMultipleAnswerSelectedOptions" parameterType="java.util.List">
|
||||
INSERT INTO tb_answer_selected_options (
|
||||
answer_id,
|
||||
option_id
|
||||
) VALUES
|
||||
<foreach collection="list" item="item" separator=",">
|
||||
(
|
||||
#{item.answerId, jdbcType=OTHER},
|
||||
#{item.optionId, jdbcType=OTHER}
|
||||
)
|
||||
</foreach>
|
||||
</insert>
|
||||
|
||||
<select id="findOptionsByAnswerId" resultMap="answerSelectedOptionResultMap">
|
||||
SELECT
|
||||
answer_id,
|
||||
option_id
|
||||
FROM tb_answer_selected_options
|
||||
WHERE answer_id = #{answerId, jdbcType=OTHER}
|
||||
</select>
|
||||
|
||||
<delete id="deleteByAnswerId">
|
||||
DELETE FROM tb_answer_selected_options
|
||||
WHERE answer_id = #{answerId, jdbcType=OTHER}
|
||||
</delete>
|
||||
|
||||
<delete id="deleteAnswerSelectedOption">
|
||||
DELETE FROM tb_answer_selected_options
|
||||
WHERE answer_id = #{param1, jdbcType=OTHER} AND option_id = #{param2, jdbcType=OTHER}
|
||||
</delete>
|
||||
</mapper>
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="sgis.surveysystem.mapper.QuestionMapper">
|
||||
|
||||
<resultMap id="questionResultMap" type="sgis.surveysystem.domain.Question">
|
||||
<id property="questionId" column="question_id"/>
|
||||
<result property="surveyId" column="survey_id"/>
|
||||
<result property="questionText" column="question_text"/>
|
||||
<result property="questionType" column="question_type"/>
|
||||
<result property="questionOrder" column="question_order"/>
|
||||
<result property="isRequired" column="is_required"/>
|
||||
</resultMap>
|
||||
|
||||
<!-- 문항 삽입 -->
|
||||
<insert id="insertQuestion" parameterType="sgis.surveysystem.domain.Question">
|
||||
INSERT INTO tb_questions (
|
||||
question_id,
|
||||
survey_id,
|
||||
question_text,
|
||||
question_type,
|
||||
question_order,
|
||||
is_required
|
||||
) VALUES (
|
||||
CAST(#{questionId, jdbcType=OTHER} AS uuid), <!-- 이 부분을 수정했습니다. -->
|
||||
CAST(#{surveyId, jdbcType=OTHER} AS uuid), <!-- 이 부분을 수정했습니다. -->
|
||||
#{questionText},
|
||||
#{questionType},
|
||||
#{questionOrder},
|
||||
#{isRequired}
|
||||
)
|
||||
</insert>
|
||||
|
||||
<select id="findQuestionsBySurveyId" resultMap="questionResultMap">
|
||||
SELECT
|
||||
question_id,
|
||||
survey_id,
|
||||
question_text,
|
||||
question_type,
|
||||
question_order,
|
||||
is_required
|
||||
FROM tb_questions
|
||||
WHERE survey_id = CAST(#{surveyId, jdbcType=OTHER} AS uuid)
|
||||
ORDER BY question_order ASC
|
||||
</select>
|
||||
|
||||
<select id="findQuestionById" resultMap="questionResultMap">
|
||||
SELECT
|
||||
question_id,
|
||||
survey_id,
|
||||
question_text,
|
||||
question_type,
|
||||
question_order,
|
||||
is_required
|
||||
FROM tb_questions
|
||||
WHERE question_id = CAST(#{questionId, jdbcType=OTHER} AS uuid)
|
||||
</select>
|
||||
|
||||
<update id="updateQuestion" parameterType="sgis.surveysystem.domain.Question">
|
||||
UPDATE tb_questions
|
||||
SET
|
||||
question_text = #{questionText},
|
||||
question_type = #{questionType},
|
||||
question_order = #{questionOrder},
|
||||
is_required = #{isRequired}
|
||||
WHERE question_id = CAST(#{questionId, jdbcType=OTHER} AS uuid)
|
||||
</update>
|
||||
|
||||
<delete id="deleteQuestion">
|
||||
DELETE FROM tb_questions
|
||||
WHERE question_id = CAST(#{questionId, jdbcType=OTHER} AS uuid)
|
||||
</delete>
|
||||
</mapper>
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="sgis.surveysystem.mapper.QuestionOptionMapper">
|
||||
|
||||
<resultMap id="questionOptionResultMap" type="sgis.surveysystem.domain.QuestionOption">
|
||||
<id property="optionId" column="option_id"/>
|
||||
<result property="questionId" column="question_id"/>
|
||||
<result property="optionText" column="option_text"/>
|
||||
<result property="optionOrder" column="option_order"/>
|
||||
</resultMap>
|
||||
|
||||
<!-- 선택지 삽입 -->
|
||||
<insert id="insertQuestionOption" parameterType="sgis.surveysystem.domain.QuestionOption">
|
||||
INSERT INTO tb_question_options (
|
||||
option_id,
|
||||
question_id,
|
||||
option_text,
|
||||
option_order
|
||||
) VALUES (
|
||||
CAST(#{optionId, jdbcType=OTHER} AS uuid), <!-- 이 부분을 수정했습니다. -->
|
||||
CAST(#{questionId, jdbcType=OTHER} AS uuid), <!-- 이 부분을 수정했습니다. -->
|
||||
#{optionText},
|
||||
#{optionOrder}
|
||||
)
|
||||
</insert>
|
||||
|
||||
<select id="findOptionsByQuestionId" resultMap="questionOptionResultMap">
|
||||
SELECT
|
||||
option_id,
|
||||
question_id,
|
||||
option_text,
|
||||
option_order
|
||||
FROM tb_question_options
|
||||
WHERE question_id = CAST(#{questionId, jdbcType=OTHER} AS uuid)
|
||||
ORDER BY option_order ASC
|
||||
</select>
|
||||
|
||||
<select id="findQuestionOptionById" resultMap="questionOptionResultMap">
|
||||
SELECT
|
||||
option_id,
|
||||
question_id,
|
||||
option_text,
|
||||
option_order
|
||||
FROM tb_question_options
|
||||
WHERE option_id = CAST(#{optionId, jdbcType=OTHER} AS uuid)
|
||||
</select>
|
||||
|
||||
<update id="updateQuestionOption" parameterType="sgis.surveysystem.domain.QuestionOption">
|
||||
UPDATE tb_question_options
|
||||
SET
|
||||
option_text = #{optionText},
|
||||
option_order = #{optionOrder}
|
||||
WHERE option_id = CAST(#{optionId, jdbcType=OTHER} AS uuid)
|
||||
</update>
|
||||
|
||||
<delete id="deleteQuestionOption">
|
||||
DELETE FROM tb_question_options
|
||||
WHERE option_id = CAST(#{optionId, jdbcType=OTHER} AS uuid)
|
||||
</delete>
|
||||
</mapper>
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="sgis.surveysystem.mapper.RespondentMapper">
|
||||
|
||||
<resultMap id="respondentResultMap" type="sgis.surveysystem.domain.Respondent">
|
||||
<id property="respondentId" column="respondent_id"/>
|
||||
<result property="userId" column="user_id"/>
|
||||
<result property="ipAddress" column="ip_address"/>
|
||||
<result property="responseStartAt" column="response_start_at"/>
|
||||
<result property="responseEndAt" column="response_end_at"/>
|
||||
</resultMap>
|
||||
|
||||
<insert id="insertRespondent" parameterType="sgis.surveysystem.domain.Respondent">
|
||||
INSERT INTO tb_respondents (
|
||||
respondent_id,
|
||||
user_id,
|
||||
ip_address,
|
||||
response_start_at,
|
||||
response_end_at
|
||||
) VALUES (
|
||||
#{respondentId, jdbcType=OTHER},
|
||||
#{userId},
|
||||
#{ipAddress, jdbcType=OTHER},
|
||||
#{responseStartAt},
|
||||
#{responseEndAt}
|
||||
)
|
||||
</insert>
|
||||
|
||||
<select id="findRespondentById" resultMap="respondentResultMap">
|
||||
SELECT
|
||||
respondent_id,
|
||||
user_id,
|
||||
ip_address,
|
||||
response_start_at,
|
||||
response_end_at
|
||||
FROM tb_respondents
|
||||
WHERE respondent_id = #{respondentId, jdbcType=OTHER}
|
||||
</select>
|
||||
|
||||
<select id="findRespondentByUserId" resultMap="respondentResultMap">
|
||||
SELECT
|
||||
respondent_id,
|
||||
user_id,
|
||||
ip_address,
|
||||
response_start_at,
|
||||
response_end_at
|
||||
FROM tb_respondents
|
||||
WHERE user_id = #{userId}
|
||||
LIMIT 1
|
||||
</select>
|
||||
|
||||
<update id="updateRespondent" parameterType="sgis.surveysystem.domain.Respondent">
|
||||
UPDATE tb_respondents
|
||||
SET
|
||||
user_id = #{userId},
|
||||
ip_address = #{ipAddress, jdbcType=OTHER},
|
||||
response_start_at = #{responseStartAt},
|
||||
response_end_at = #{responseEndAt}
|
||||
WHERE respondent_id = #{respondentId, jdbcType=OTHER}
|
||||
</update>
|
||||
|
||||
<delete id="deleteRespondent">
|
||||
DELETE FROM tb_respondents
|
||||
WHERE respondent_id = #{respondentId, jdbcType=OTHER}
|
||||
</delete>
|
||||
</mapper>
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="sgis.surveysystem.mapper.SurveyMapper">
|
||||
|
||||
<resultMap id="surveyResultMap" type="sgis.surveysystem.domain.Survey">
|
||||
<id property="surveyId" column="survey_id"/>
|
||||
<result property="surveyTitle" column="survey_title"/>
|
||||
<result property="surveyDescription" column="survey_description"/>
|
||||
<result property="createdAt" column="created_at"/>
|
||||
<result property="updatedAt" column="updated_at"/>
|
||||
<result property="isActive" column="is_active"/>
|
||||
<result property="startDate" column="start_date"/>
|
||||
<result property="endDate" column="end_date"/>
|
||||
</resultMap>
|
||||
|
||||
<insert id="insertSurvey" parameterType="sgis.surveysystem.domain.Survey">
|
||||
INSERT INTO tb_surveys (
|
||||
survey_id,
|
||||
survey_title,
|
||||
survey_description,
|
||||
created_at,
|
||||
updated_at,
|
||||
is_active,
|
||||
start_date,
|
||||
end_date
|
||||
) VALUES (
|
||||
CAST(#{surveyId, jdbcType=OTHER} AS uuid),
|
||||
#{surveyTitle},
|
||||
#{surveyDescription},
|
||||
#{createdAt},
|
||||
#{updatedAt},
|
||||
#{isActive},
|
||||
#{startDate},
|
||||
#{endDate}
|
||||
)
|
||||
</insert>
|
||||
|
||||
<select id="findAllSurveys" resultMap="surveyResultMap">
|
||||
SELECT
|
||||
survey_id,
|
||||
survey_title,
|
||||
survey_description,
|
||||
created_at,
|
||||
updated_at,
|
||||
is_active,
|
||||
start_date,
|
||||
end_date
|
||||
FROM tb_surveys
|
||||
</select>
|
||||
|
||||
<select id="findSurveyById" resultMap="surveyResultMap">
|
||||
SELECT
|
||||
survey_id,
|
||||
survey_description,
|
||||
survey_title,
|
||||
created_at,
|
||||
updated_at,
|
||||
is_active,
|
||||
start_date,
|
||||
end_date
|
||||
FROM tb_surveys
|
||||
WHERE survey_id = CAST(#{surveyId, jdbcType=OTHER} AS uuid) <!-- 이 부분을 수정했습니다. -->
|
||||
</select>
|
||||
|
||||
<update id="updateSurvey" parameterType="sgis.surveysystem.domain.Survey">
|
||||
UPDATE tb_surveys
|
||||
SET
|
||||
survey_title = #{surveyTitle},
|
||||
survey_description = #{surveyDescription},
|
||||
updated_at = #{updatedAt},
|
||||
is_active = #{isActive},
|
||||
start_date = #{startDate},
|
||||
end_date = #{endDate}
|
||||
WHERE survey_id = CAST(#{surveyId, jdbcType=OTHER} AS uuid) <!-- 이 부분도 일관성을 위해 수정합니다. -->
|
||||
</update>
|
||||
|
||||
<update id="updateSurveyStatus">
|
||||
UPDATE tb_surveys
|
||||
SET
|
||||
is_active = #{isActive},
|
||||
updated_at = NOW()
|
||||
WHERE survey_id = CAST(#{surveyId, jdbcType=OTHER} AS uuid) <!-- 이 부분도 일관성을 위해 수정합니다. -->
|
||||
</update>
|
||||
|
||||
<delete id="deleteSurvey">
|
||||
DELETE FROM tb_surveys
|
||||
WHERE survey_id = CAST(#{surveyId, jdbcType=OTHER} AS uuid) <!-- 이 부분도 일관성을 위해 수정합니다. -->
|
||||
</delete>
|
||||
</mapper>
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="sgis.surveysystem.mapper.SurveyResponseMapper">
|
||||
|
||||
<resultMap id="surveyResponseResultMap" type="sgis.surveysystem.domain.SurveyResponse">
|
||||
<id property="responseId" column="response_id"/>
|
||||
<result property="surveyId" column="survey_id"/>
|
||||
<result property="respondentId" column="respondent_id"/>
|
||||
<result property="submittedAt" column="submitted_at"/>
|
||||
</resultMap>
|
||||
|
||||
<insert id="insertSurveyResponse" parameterType="sgis.surveysystem.domain.SurveyResponse">
|
||||
INSERT INTO tb_survey_responses (
|
||||
response_id,
|
||||
survey_id,
|
||||
respondent_id,
|
||||
submitted_at
|
||||
) VALUES (
|
||||
#{responseId, jdbcType=OTHER},
|
||||
#{surveyId, jdbcType=OTHER},
|
||||
#{respondentId, jdbcType=OTHER},
|
||||
#{submittedAt}
|
||||
)
|
||||
</insert>
|
||||
|
||||
<select id="findSurveyResponseById" resultMap="surveyResponseResultMap">
|
||||
SELECT
|
||||
response_id,
|
||||
survey_id,
|
||||
respondent_id,
|
||||
submitted_at
|
||||
FROM tb_survey_responses
|
||||
WHERE response_id = #{responseId, jdbcType=OTHER}
|
||||
</select>
|
||||
|
||||
<select id="findResponsesBySurveyId" resultMap="surveyResponseResultMap">
|
||||
SELECT
|
||||
response_id,
|
||||
survey_id,
|
||||
respondent_id,
|
||||
submitted_at
|
||||
FROM tb_survey_responses
|
||||
WHERE survey_id = #{surveyId, jdbcType=OTHER}
|
||||
ORDER BY submitted_at DESC
|
||||
</select>
|
||||
|
||||
<select id="findResponsesByRespondentId" resultMap="surveyResponseResultMap">
|
||||
SELECT
|
||||
response_id,
|
||||
survey_id,
|
||||
respondent_id,
|
||||
submitted_at
|
||||
FROM tb_survey_responses
|
||||
WHERE respondent_id = #{respondentId, jdbcType=OTHER}
|
||||
ORDER BY submitted_at DESC
|
||||
</select>
|
||||
|
||||
<delete id="deleteSurveyResponse">
|
||||
DELETE FROM tb_survey_responses
|
||||
WHERE response_id = #{responseId, jdbcType=OTHER}
|
||||
</delete>
|
||||
</mapper>
|
||||
|
|
@ -19,7 +19,9 @@
|
|||
<property name="typeHandlers">
|
||||
<array>
|
||||
<bean class="sgis.board.typehandler.OffsetDateTimeTypeHandler" />
|
||||
</array>
|
||||
<bean class="sgis.surveysystem.config.UUIDTypeHandler" />
|
||||
<bean class="sgis.surveysystem.config.LocalDateTimeTypeHandler" />
|
||||
</array>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -20,7 +20,14 @@ function loadList(){
|
|||
url : "/sgis/portal/board/all.do?boardCategoryId=${boardCategoryId}",
|
||||
type : "get",
|
||||
dataType : "json",
|
||||
success : makeView,
|
||||
success : function(result) {
|
||||
try {
|
||||
var jsonData = JSON.parse(result);
|
||||
makeView(jsonData)
|
||||
} catch(e) {
|
||||
console.log("Error occured while parsing JSON")
|
||||
}
|
||||
},
|
||||
error : function(){
|
||||
alert("error");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,6 +80,17 @@
|
|||
<td><input type="text" id="title" name="title" class="form-control"
|
||||
<c:if test="${parentPostId != null && parentPostId != ''}">readonly="readonly"</c:if>/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="td-head" scope="row">파일첨부</th>
|
||||
<!-- <td><input type="file" id="title" name="title" class="form-control" /></td> -->
|
||||
<td>
|
||||
<div id="dropZone" class="form-control p-4" style="cursor:pointer;">
|
||||
<p class="text-muted">여기에 파일을 드래그 앤 드롭 하거나 클릭해서 파일 선택</p>
|
||||
<input type="file" id="fileInput" multiple hidden />
|
||||
<ul id="fileList" class="list-group"></ul>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="td-head" scope="row">내용</th>
|
||||
<td><textarea rows="7" class="form-control" id="boardContent" name="boardContent"></textarea></td>
|
||||
|
|
@ -101,7 +112,9 @@
|
|||
<!-- 쓰기 끝 -->
|
||||
|
||||
<script>
|
||||
let fileList = []; // 파일첨부 - 첨부된 파일 목록 포시란
|
||||
$(document).ready(function() {
|
||||
|
||||
var parentPostId = "${parentPostId}";
|
||||
if (parentPostId !== null && parentPostId !== '' && parentPostId !== "null") {
|
||||
// 부모글 정보 로드
|
||||
|
|
@ -125,6 +138,67 @@ $(document).ready(function() {
|
|||
}
|
||||
});
|
||||
}
|
||||
const $dropZone = $("#dropZone"); // 파일첨부 - 드래그앤드롭
|
||||
const $fileInput = $("#fileInput"); // 파일첨부 -
|
||||
const $fileListUI = $("#fileList"); // 첨부된 파일 목록 표시
|
||||
|
||||
// 파일 선택 창 열기
|
||||
$dropZone.on("click", function () {
|
||||
$fileInput.trigger("click");
|
||||
});
|
||||
|
||||
// 드래그 오버 스타일
|
||||
$dropZone.on("dragover", function (e) {
|
||||
e.preventDefault();
|
||||
$dropZone.addClass("dragover");
|
||||
});
|
||||
|
||||
$dropZone.on("dragleave", function () {
|
||||
$dropZone.removeClass("dragover");
|
||||
});
|
||||
|
||||
$dropZone.on("drop", function (e) {
|
||||
e.preventDefault();
|
||||
$dropZone.removeClass("dragover");
|
||||
const files = e.originalEvent.dataTransfer.files;
|
||||
handleFiles(files);
|
||||
});
|
||||
|
||||
$fileInput.on("change", function () {
|
||||
handleFiles(this.files);
|
||||
});
|
||||
|
||||
function handleFiles(files) {
|
||||
$.each(files, function (i, file) {
|
||||
// 중복 체크는 이름으로만 (간단히 처리)
|
||||
if (!fileList.some(f => f.name === file.name && f.size === file.size)) {
|
||||
fileList.push(file);
|
||||
}
|
||||
});
|
||||
renderFileList();
|
||||
}
|
||||
|
||||
function renderFileList() {
|
||||
$fileListUI.empty();
|
||||
$.each(fileList, function (index, file) {
|
||||
const $li = $(`
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
<span>` + file.name + ` </span>
|
||||
<b class="icon-trash remove-btn" title="삭제버튼" style="cursor:pointer;">삭제</b>
|
||||
</li>
|
||||
`);
|
||||
$li.find(".remove-btn").on("click", function () {
|
||||
removeFile(index);
|
||||
});
|
||||
$fileListUI.append($li);
|
||||
});
|
||||
}
|
||||
|
||||
function removeFile(index) {
|
||||
fileList.splice(index, 1);
|
||||
renderFileList();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
function goList(){
|
||||
|
|
@ -171,4 +245,49 @@ function goInsert(){
|
|||
});
|
||||
$(".fclear").trigger("click");
|
||||
}
|
||||
|
||||
|
||||
function addPost(){
|
||||
var title = $("#title").val();
|
||||
var content = $("#boardContent").val();
|
||||
var boardCategoryId = $("#boardCategoryId").val();
|
||||
var parentPostId = $("#parentPostId").val();
|
||||
|
||||
if(title == null || title == "" || title == 0){
|
||||
alert("제목을 입력하세요");
|
||||
return false;
|
||||
} else if (content == null || content == "" || content == 0) {
|
||||
alert("내용을 입력하세요");
|
||||
return false;
|
||||
}
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append("title", title);
|
||||
formData.append("content", content);
|
||||
formData.append("boardCategoryId", boardCategoryId);
|
||||
|
||||
if (parentPostId !== null && parentPostId !== "" && parentPostId !== "null") {
|
||||
formData.append("parentPostId", parentPostId);
|
||||
}
|
||||
// $.each(fileList, function (i, file) {
|
||||
// formData.append("files", file);
|
||||
// });
|
||||
|
||||
$.ajax({
|
||||
url : "/sgis/portal/board/new.do",
|
||||
type : "post",
|
||||
data: formData,
|
||||
contentType: false,
|
||||
processData: false,
|
||||
success : function(){
|
||||
$("#grid").empty();
|
||||
var categoryId = "${boardCategoryId}" ? "${boardCategoryId}" : "${params.boardCategoryId}";
|
||||
location.href = "/sgis/portal/board-list.do?boardCategoryId=" + categoryId;
|
||||
},
|
||||
error : function() {
|
||||
alert("error");
|
||||
}
|
||||
});
|
||||
$(".fclear").trigger("click");
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@
|
|||
<a href="javascript:void(0);">스마트지반정보관리 시스템 소개</a>
|
||||
</li>
|
||||
<li class="mega-menu-dropdown">
|
||||
<a href="board-list.do?boardCategoryId=${params.boardCategoryId}"> 커뮤니티 </a>
|
||||
<a href="board-list.do?boardCategoryId=${params.boardCategoryId}"> 자료실 </a>
|
||||
</li>
|
||||
<li class="mega-menu-dropdown">
|
||||
<a href="boardNotice.do"> 공지사항 </a>
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,358 @@
|
|||
<%@ 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 PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
|
||||
<title>스마트지반정보관리 시스템</title>
|
||||
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name = "viewport" content = "user-scalable=no, width=device-width">
|
||||
<meta name="author" content="해양수산부">
|
||||
<!-- description -->
|
||||
<meta name="description" content="스마트지반정보관리">
|
||||
<!-- keywords -->
|
||||
<meta name="keywords" content="">
|
||||
<!-- favicon -->
|
||||
<link rel="shortcut icon" href="${pageContext.request.contextPath}/com/img/common/icon/favicon.ico">
|
||||
|
||||
<script src="${pageContext.request.contextPath}/com/plugins/kendo-ui/js/jquery.js"></script>
|
||||
|
||||
|
||||
<script src="${pageContext.request.contextPath}/com/plugins/bootstrap.min.js"></script>
|
||||
<link rel="stylesheet" href="${pageContext.request.contextPath}/com/plugins/bootstrap.min.css" />
|
||||
|
||||
<script src="${pageContext.request.contextPath}/com/plugins/css-element-queries/ResizeSensor.js"></script>
|
||||
<script src="${pageContext.request.contextPath}/com/plugins/css-element-queries/ElementQueries.js"></script>
|
||||
|
||||
<script src="${pageContext.request.contextPath}/com/js/app.js"></script>
|
||||
<script src="${pageContext.request.contextPath}/com/js/cross-section.js"></script>
|
||||
|
||||
<!-- 스타일 css -->
|
||||
<link rel="stylesheet" href="${pageContext.request.contextPath}/com/css/style.css" />
|
||||
|
||||
<link rel="stylesheet" href="${pageContext.request.contextPath}/com/css/common.v2.0.css" />
|
||||
|
||||
<link rel="stylesheet" href="${pageContext.request.contextPath}/com/css/cross-section.css" />
|
||||
|
||||
|
||||
<link rel="stylesheet" href="${pageContext.request.contextPath}/com/plugins/openlayers/theme/default/style.css" type="text/css" />
|
||||
<script type="text/javascript" src="${pageContext.request.contextPath}/com/plugins/proj4js/proj4js-compressed.js"></script>
|
||||
<script type="text/javascript" src="${pageContext.request.contextPath}/com/plugins/openlayers/OpenLayers.js"></script>
|
||||
|
||||
<script type="text/javascript" src="${pageContext.request.contextPath}/com/js/map/sichudan/js/json2.js"></script>
|
||||
<script type="text/javascript" src="${pageContext.request.contextPath}/com/js/map/main/section.js" charset="UTF-8"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
// --------------------------------
|
||||
// 시스템 변수
|
||||
// --------------------------------
|
||||
var geoSection = null;
|
||||
|
||||
// --------------------------------
|
||||
// 어플리케이션 로딩
|
||||
// --------------------------------
|
||||
addLoadEvent(initApp);
|
||||
function initApp() {
|
||||
|
||||
showProgress();
|
||||
|
||||
if (geoSection == null) {
|
||||
geoSection = new GeoSection("map");
|
||||
}
|
||||
|
||||
var data = "${data}";
|
||||
geoSection.show(data);
|
||||
|
||||
hideProgress();
|
||||
|
||||
}
|
||||
|
||||
// --------------------------------
|
||||
// 함수 : 지반 단면도 보기 이벤트 컨트롤
|
||||
// --------------------------------
|
||||
var isShowAltitude = false;
|
||||
var isShowDistance = false;
|
||||
var isShowSPT = false;
|
||||
var isShowLink = false;
|
||||
var isShowWater = false;
|
||||
function zoomToExtent() {
|
||||
geoSection.zoomToExtent();
|
||||
}
|
||||
function zoomIn() {
|
||||
geoSection.zoomIn();
|
||||
}
|
||||
function zoomOut() {
|
||||
geoSection.zoomOut();
|
||||
}
|
||||
function toggleAltitude() {
|
||||
if (isShowAltitude) {
|
||||
isShowAltitude = false;
|
||||
geoSection.hideHoleAltitude();
|
||||
} else {
|
||||
isShowAltitude = true;
|
||||
geoSection.showHoleAltitude();
|
||||
}
|
||||
}
|
||||
function toggleDistance() {
|
||||
if (isShowDistance) {
|
||||
isShowDistance = false;
|
||||
geoSection.hideHoleDistance();
|
||||
} else {
|
||||
isShowDistance = true;
|
||||
geoSection.showHoleDistance();
|
||||
}
|
||||
}
|
||||
function toggleSPT() {
|
||||
if (isShowSPT) {
|
||||
isShowSPT = false;
|
||||
geoSection.hideHoleSPT();
|
||||
} else {
|
||||
isShowSPT = true;
|
||||
geoSection.showHoleSPT();
|
||||
}
|
||||
}
|
||||
function toggleLink() {
|
||||
if (isShowLink) {
|
||||
isShowLink = false;
|
||||
geoSection.hideHoleLink();
|
||||
} else {
|
||||
isShowLink = true;
|
||||
geoSection.showHoleLink();
|
||||
}
|
||||
}
|
||||
function toggleWater() {
|
||||
if (isShowWater) {
|
||||
isShowWater = false;
|
||||
geoSection.hideHoleWater();
|
||||
} else {
|
||||
isShowWater = true;
|
||||
geoSection.showHoleWater();
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------
|
||||
// 함수 : Process 패널 보이기 & 숨기기
|
||||
// --------------------------------
|
||||
var proWin = null;
|
||||
function showProgress() {
|
||||
var over = document.getElementById("over");
|
||||
if (over != null) {
|
||||
over.style.display = "block";
|
||||
}
|
||||
}
|
||||
function hideProgress() {
|
||||
var over = document.getElementById("over");
|
||||
if (over != null) {
|
||||
over.style.display = "none";
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body class="map-service cross-section">
|
||||
|
||||
<!-- 페이지 컨테이너 시작 -->
|
||||
<section class="page-container">
|
||||
|
||||
<div class="page-content-wrapper">
|
||||
|
||||
<!-- 컨텐츠 시작 -->
|
||||
<div class="page-content">
|
||||
|
||||
<div class="page-content-inner">
|
||||
|
||||
<div class="page-title-wrapper">
|
||||
<h1 class="cross-section-title"><span>지반 단면도</span></h1>
|
||||
</div>
|
||||
|
||||
<div class="cross-section-wrapper">
|
||||
<div class="cross-contents-panel">
|
||||
<!-- <div id="map" class="cross-contents-map" style="background-color: white; border: 1px solid black; padding: 0px;">
|
||||
</div>
|
||||
<div id="over" align="center" style="position: absolute; top: 120px; left: 400px; width: 751px; height: 517px;">
|
||||
<img src="/com/img/map-service/sichudan/loading.gif" style="position: relative; top: 130px;">
|
||||
</div> -->
|
||||
|
||||
<div id="map" style="width: 100%; height: calc(100vh - 296px); background-color: white; border: 1px solid black; padding: 0px;"></div>
|
||||
<div id="over" align="center" style="position: absolute; top: calc(100vh - 85%); left: auto; width: 100%; height: calc(100vh - 296px);">
|
||||
<img src="/com/img/map-service/sichudan/loading.gif" style="position: relative; top: auto;">
|
||||
</div>
|
||||
<div class="cross-contents-option">
|
||||
<div class="cross-option-box">
|
||||
<div class="cross-option-col cross-option-ex">
|
||||
<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-info">지반단면도에 대한 XML 데이터를 보실 수 있습니다.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="cross-option-col cross-btn-group">
|
||||
<ul>
|
||||
<li>
|
||||
<button type="button" class="btn btn-small btn-rounded5 btn-ske-blue btn-icon-left btn-icon-zoom-all" onclick="zoomToExtent();" onfocus="this.blur();"><span>전체</span></button>
|
||||
<button type="button" class="btn btn-small btn-rounded5 btn-ske-blue btn-icon-left btn-icon-zoom-plus" onclick="zoomIn();" onfocus="this.blur();"><span>확대</span></button>
|
||||
<button type="button" class="btn btn-small btn-rounded5 btn-ske-blue btn-icon-left btn-icon-zoom-minus" onclick="zoomOut();" onfocus="this.blur();"><span>축소</span></button>
|
||||
</li>
|
||||
<li>
|
||||
<button type="button" class="btn btn-small btn-rounded5 btn-dark-gray btn-icon-left btn-icon-nextB" onclick="toggleAltitude();" onfocus="this.blur();"><span>표고정보보기</span></button>
|
||||
<button type="button" class="btn btn-small btn-rounded5 btn-dark-gray btn-icon-left btn-icon-nextB" onclick="toggleDistance();" onfocus="this.blur();"><span>시추공사이거리보기</span></button>
|
||||
</li>
|
||||
<li>
|
||||
<button type="button" class="btn btn-small btn-rounded5 btn-dark-gray btn-icon-left btn-icon-nextB" onclick="toggleSPT();" onfocus="this.blur();"><span>SPT보기</span></button>
|
||||
<button type="button" class="btn btn-small btn-rounded5 btn-dark-gray btn-icon-left btn-icon-nextB" onclick="toggleLink();" onfocus="this.blur();"><span>지층연결</span></button>
|
||||
<button type="button" class="btn btn-small btn-rounded5 btn-dark-gray btn-icon-left btn-icon-nextB" onclick="toggleWater();" onfocus="this.blur();"><span>지하수위보기</span></button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cross-legend-panel">
|
||||
<h2 class="page-title-1depth"><span>지층 색상표</span></h2>
|
||||
|
||||
<div class="table-scrollable color-palette">
|
||||
<table class="table table-bordered">
|
||||
<colgroup>
|
||||
<col style="width:40px;">
|
||||
<col style="width:60px;">
|
||||
<col style="width:40px;">
|
||||
<col style="width:auto;">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<th>색상</th>
|
||||
<th>USCS분류</th>
|
||||
<th colspan="2">지층명</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<tr class="palette-tr-g">
|
||||
<td rowspan="4" class="palette-td-g"></td>
|
||||
<td>GW</td>
|
||||
<td rowspan="4">자갈</td>
|
||||
<td class="t-left">입도 좋은 자갈</td>
|
||||
</tr>
|
||||
<tr class="palette-tr-g">
|
||||
<td>GP</td>
|
||||
<td class="t-left">입도 나쁜 자갈</td>
|
||||
</tr>
|
||||
<tr class="palette-tr-g">
|
||||
<td>GM</td>
|
||||
<td class="t-left">실트질 자갈</td>
|
||||
</tr>
|
||||
<tr class="palette-tr-g">
|
||||
<td>GC</td>
|
||||
<td class="t-left">점토질 자갈</td>
|
||||
</tr>
|
||||
|
||||
<tr class="palette-tr-s">
|
||||
<td rowspan="4" class="palette-td-s"></td>
|
||||
<td>SW</td>
|
||||
<td rowspan="4">모래</td>
|
||||
<td class="t-left">입도 좋은 모래</td>
|
||||
</tr>
|
||||
<tr class="palette-tr-s">
|
||||
<td>SP</td>
|
||||
<td class="t-left">입도 나쁜 모래</td>
|
||||
</tr>
|
||||
<tr class="palette-tr-s">
|
||||
<td>SM</td>
|
||||
<td class="t-left">실트질 모래</td>
|
||||
</tr>
|
||||
<tr class="palette-tr-s">
|
||||
<td>SC</td>
|
||||
<td class="t-left">점토질 모래</td>
|
||||
</tr>
|
||||
|
||||
<tr class="palette-tr-mc">
|
||||
<td rowspan="4" class="palette-td-mc"></td>
|
||||
<td>ML</td>
|
||||
<td rowspan="2">실트</td>
|
||||
<td class="t-left">무기질 실트, 극세사</td>
|
||||
</tr>
|
||||
<tr class="palette-tr-mc">
|
||||
<td>MH</td>
|
||||
<td class="t-left">무기질 실트</td>
|
||||
</tr>
|
||||
<tr class="palette-tr-mc">
|
||||
<td>CL</td>
|
||||
<td rowspan="2">점토</td>
|
||||
<td class="t-left">무기질, 자갈질 점토</td>
|
||||
</tr>
|
||||
<tr class="palette-tr-mc">
|
||||
<td>CH</td>
|
||||
<td class="t-left">고소성 무기질 점토</td>
|
||||
</tr>
|
||||
|
||||
<tr class="palette-tr-op">
|
||||
<td rowspan="3" class="palette-td-op"></td>
|
||||
<td>OH</td>
|
||||
<td colspan="2" class="t-left">중소성 유기질 점토</td>
|
||||
</tr>
|
||||
<tr class="palette-tr-op">
|
||||
<td>OL</td>
|
||||
<td colspan="2" class="t-left">유기질 실트</td>
|
||||
</tr>
|
||||
<tr class="palette-tr-op">
|
||||
<td>PT</td>
|
||||
<td colspan="2" class="t-left">이탄 및 고유기질토</td>
|
||||
</tr>
|
||||
|
||||
<tr class="palette-tr-wr">
|
||||
<td class="palette-td-wr"></td>
|
||||
<td>WR</td>
|
||||
<td colspan="2" class="t-left">풍화암</td>
|
||||
</tr>
|
||||
|
||||
<tr class="palette-tr-sr">
|
||||
<td class="palette-td-sr"></td>
|
||||
<td>SR</td>
|
||||
<td colspan="2" class="t-left">연암</td>
|
||||
</tr>
|
||||
|
||||
<tr class="palette-tr-mr">
|
||||
<td class="palette-td-mr"></td>
|
||||
<td>MR</td>
|
||||
<td colspan="2" class="t-left">보통암</td>
|
||||
</tr>
|
||||
|
||||
<tr class="palette-tr-hr">
|
||||
<td class="palette-td-hr"></td>
|
||||
<td>HR</td>
|
||||
<td colspan="2" class="t-left">경암</td>
|
||||
</tr>
|
||||
|
||||
<tr class="palette-tr-xr">
|
||||
<td class="palette-td-xr"></td>
|
||||
<td>XR</td>
|
||||
<td colspan="2" class="t-left">극경암</td>
|
||||
</tr>
|
||||
|
||||
<tr class="palette-tr-et">
|
||||
<td class="palette-td-et"></td>
|
||||
<td>ET</td>
|
||||
<td colspan="2" class="t-left">기타</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- 컨텐츠 끝 -->
|
||||
|
||||
</div>
|
||||
|
||||
</section>
|
||||
<!-- 페이지 컨테이너 끝 -->
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -181,7 +181,7 @@
|
|||
}
|
||||
$(document).ready(function () {
|
||||
$("#projectStartDate").kendoDatePicker({
|
||||
value : new Date(),
|
||||
value : new Date("1990-01-01T00:00:00.000+09:00"),
|
||||
culture: "ko-KR",
|
||||
format : "yyyy-MM-dd"
|
||||
});
|
||||
|
|
@ -256,7 +256,7 @@
|
|||
var business_process_data = ${codeJson_business_process_code}; // 사업 공정 코드
|
||||
$(document).ready(function () {
|
||||
$("#inputYmdTo").kendoDatePicker({
|
||||
value : new Date(),
|
||||
value : new Date("1990-01-01T00:00:00.000+09:00"),
|
||||
culture: "ko-KR",
|
||||
format : "yyyy-MM-dd"
|
||||
});
|
||||
|
|
@ -368,17 +368,51 @@
|
|||
<input type="radio" name="sichuSelect" id="polygon" autocomplete="off" onchange="changeInteraction('areaClick');" > 영역선택
|
||||
</label>
|
||||
</div>
|
||||
</li>
|
||||
<li style="width: 400px;">
|
||||
<button type="button" id="searchInfo" class="btn btn-ske-blue btn-icon-left btn-icon-search" ><span>정보조회</span></button>
|
||||
<a href="javascript:void(0);" id="mapDemSearch" class="btn btn-single-toggle" data-toggle="button" aria-pressed="false" autocomplete="off" ><span>DEM 시계열 조회</span></a>
|
||||
<script>
|
||||
$('#mapDemSearch').click(function(){
|
||||
$(".calendar-group").toggleClass('active');
|
||||
$(".map-bottom-control-wrapper").toggleClass('active');
|
||||
});
|
||||
</script>
|
||||
</li>
|
||||
</li>
|
||||
<li style="width: 400px;">
|
||||
<button type="button" id="searchInfo" class="btn btn-ske-blue btn-icon-left btn-icon-search" ><span>정보조회</span></button>
|
||||
<a href="javascript:void(0);" id="mapDemSearch" class="btn btn-single-toggle" data-toggle="button" aria-pressed="false" autocomplete="off" ><span>DEM 시계열 조회</span></a>
|
||||
<script>
|
||||
$('#mapDemSearch').click(function(){
|
||||
$(".calendar-group").toggleClass('active');
|
||||
$(".map-bottom-control-wrapper").toggleClass('active');
|
||||
});
|
||||
</script>
|
||||
</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>
|
||||
<script>
|
||||
|
||||
function showSichudan() {
|
||||
var codes = getSelectHoleInfo();
|
||||
if (codes == "") {
|
||||
alert("시추공을 선택해주세요.");
|
||||
return;
|
||||
} else if(codes.indexOf('undefined') != -1 ) {
|
||||
alert("시추공이 존재하지 않는 사업입니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
var sichuWin = window.open("/map/sichudanNew.do?code=" + codes, "sichuWin", "scrollbars=no,titlebar=no,width=1024,height=740,left=0,top=0", true);
|
||||
sichuWin.focus();
|
||||
}
|
||||
|
||||
function showBoreholeLog() {
|
||||
var codes = getSelectHoleInfo();
|
||||
if (codes == "") {
|
||||
alert("시추공을 선택해주세요.");
|
||||
return;
|
||||
} else if(codes.indexOf('undefined') != -1 ) {
|
||||
alert("시추공이 존재하지 않는 사업입니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
var sichuWin = window.open("/map/borehole-log.do?code=" + codes, "Borehole log", "scrollbars=no,titlebar=no,width=1024,height=740,left=0,top=0", true);
|
||||
sichuWin.focus();
|
||||
}
|
||||
</script>
|
||||
</li>
|
||||
<li>
|
||||
<div class="form-inline calendar-group">
|
||||
<div class="input-group">
|
||||
|
|
@ -984,7 +1018,7 @@
|
|||
</div>
|
||||
<!-- 팝업 끝 -->
|
||||
|
||||
<!-- 팝업 시작 -->
|
||||
<!-- 팝업 시작 -->
|
||||
<div id="watLevInfoPopup" class="kc-popup kc-popup-btn-group" style="display:none;"> <!--하단 버튼그룹 있는 팝업은 "kc-popup-btn-group" class 추가 -->
|
||||
<div class="k-popup-edit-form button-group-display">
|
||||
<div class="k-edit-form-container">
|
||||
|
|
@ -1005,6 +1039,7 @@
|
|||
</div>
|
||||
<!-- 팝업 끝 -->
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
|
|
|||
|
|
@ -7,59 +7,8 @@
|
|||
|
||||
|
||||
</script>
|
||||
${vo.idx}, ${vo.writer}
|
||||
<!-- visual 시작 -->
|
||||
<div class="main-visual">
|
||||
<div class="main-visual-inner">
|
||||
<div class="main-visual-row">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- visual 끝 -->
|
||||
|
||||
<!-- 하단 컨텐츠 시작 -->
|
||||
<div class="row-group bottom-contents-wrap">
|
||||
<div class="row-group-inner">
|
||||
<div class="bottom-contents-row">
|
||||
<div class="bottom-con-panel faq-contents" style="max-width: 50%;">
|
||||
<div class="main-title-wrap">
|
||||
<div class="main-title">공지사항</div>
|
||||
</div>
|
||||
<div class="faq-box">
|
||||
<ul>
|
||||
<c:forEach var="vo" items="${list}">
|
||||
<li>
|
||||
<span class="faq-list-division division-blue">${vo.indate}</span>
|
||||
<a href ='<c:url value="/sgis/portal/noticeView.do"/>?idx=${vo.idx}&objID=${vo.writer}'>
|
||||
<span class="faq-list-text">${vo.title}</span>
|
||||
</a>
|
||||
</li>
|
||||
</c:forEach>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bottom-con-panel service-contents" style="max-width: 50%;">
|
||||
<div class="main-title-wrap">
|
||||
<div class="main-title">커뮤니티</div>
|
||||
</div>
|
||||
<div class="faq-box">
|
||||
<ul>
|
||||
<c:forEach var="Bvo" items="${boardList}">
|
||||
<li>
|
||||
<span class="faq-list-division division-blue">${Bvo.indate}</span>
|
||||
<a href ='<c:url value="/sgis/portal/boardView.do"/>?idx=${Bvo.idx}&objID=${Bvo.writer}'>
|
||||
<span class="faq-list-text">${Bvo.title}</span>
|
||||
</a>
|
||||
</li>
|
||||
</c:forEach>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 하단 컨텐츠 끝 -->
|
||||
${vo.idx} ${vo.writer}
|
||||
<img src="../../com/img/common/main/temp_dummy_main.png" style="margin: 0px auto;"/>
|
||||
|
||||
<!-- 실패 메세지를 출력(modal) -->
|
||||
<div id="myMessage" class="modal fade" role="dialog" >
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -0,0 +1,200 @@
|
|||
<%@ 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;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
}
|
||||
.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="datetime-local"],
|
||||
.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="datetime-local"]:focus,
|
||||
.form-group textarea:focus {
|
||||
border-color: #007bff;
|
||||
outline: none;
|
||||
}
|
||||
.form-group textarea {
|
||||
resize: vertical;
|
||||
min-height: 100px;
|
||||
}
|
||||
button {
|
||||
background-color: #007bff;
|
||||
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: #0056b3;
|
||||
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;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h2>새 설문 생성</h2>
|
||||
<form id="createSurveyForm">
|
||||
<div class="form-group">
|
||||
<label for="surveyTitle">설문 제목:</label>
|
||||
<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="설문에 대한 자세한 설명을 입력하세요.">안녕하십니까?
|
||||
저희는 사용자 여러분의 소중한 의견을 경청하고, 현재 사용하고 계신 시스템의 만족도를 면밀히 파악하여 더 나은 서비스와 환경을 제공하고자 본 설문조사를 실시하게 되었습니다.
|
||||
본 설문은 2025년 한 해 동안 시스템을 이용하면서 경험하셨던 전반적인 만족도, 사용 편의성, 기능 유용성, 안정성 등에 대한 사용자님의 솔직한 평가를 듣기 위함입니다. 여러분의 피드백은 시스템 개선의 중요한 밑거름이 될 것이며, 향후 시스템 개발 및 운영 방향 설정에 큰 도움이 될 것입니다.
|
||||
설문에 참여해주신 모든 분들께 진심으로 감사드립니다. 설문 응답은 통계적인 목적으로만 활용되며, 개인 정보는 철저히 보호됩니다.
|
||||
참여 대상: 2025년 시스템 사용자 전체소요 시간: 약 5~10분
|
||||
여러분의 적극적인 참여를 부탁드립니다.</textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="startDate">시작일시:</label>
|
||||
<input type="datetime-local" id="startDate" name="startDate">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="endDate">종료일시:</label>
|
||||
<input type="datetime-local" id="endDate" name="endDate">
|
||||
</div>
|
||||
|
||||
<button type="submit">설문 생성</button>
|
||||
</form>
|
||||
<div id="message" class="message"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.getElementById('createSurveyForm').addEventListener('submit', async function(event) {
|
||||
event.preventDefault(); // 폼의 기본 제출 동작 방지
|
||||
|
||||
const messageDiv = document.getElementById('message');
|
||||
messageDiv.style.display = 'none'; // 이전 메시지 숨기기
|
||||
messageDiv.classList.remove('success', 'error'); // 이전 스타일 클래스 제거
|
||||
|
||||
// 폼 필드에서 데이터 수집
|
||||
const formData = {
|
||||
surveyTitle: document.getElementById('surveyTitle').value,
|
||||
surveyDescription: document.getElementById('surveyDescription').value,
|
||||
// datetime-local 입력은 값이 없으면 빈 문자열이 되므로, null로 변환
|
||||
startDate: document.getElementById('startDate').value ? document.getElementById('startDate').value + ':00' : null, // 초까지 포함하도록 포맷팅
|
||||
endDate: document.getElementById('endDate').value ? document.getElementById('endDate').value + ':00' : null // 초까지 포함하도록 포맷팅
|
||||
};
|
||||
|
||||
// 컨텍스트 경로를 동적으로 가져오는 방법 (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',
|
||||
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.surveyId + ')';
|
||||
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 || '알 수 없는 오류');
|
||||
messageDiv.classList.add('error');
|
||||
messageDiv.style.display = 'block';
|
||||
}
|
||||
} catch (error) {
|
||||
// 네트워크 오류 또는 fetch 요청 자체의 문제 발생 시
|
||||
console.error('네트워크 오류:', error);
|
||||
messageDiv.textContent = '네트워크 오류가 발생했습니다. 다시 시도해주세요.';
|
||||
messageDiv.classList.add('error');
|
||||
messageDiv.style.display = 'block';
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -330,7 +330,7 @@
|
|||
<script>
|
||||
$(document).ready(function () {
|
||||
$("#inputYmdTo").kendoDatePicker({
|
||||
value : new Date(),
|
||||
value : new Date("1990-01-01T00:00:00.000+09:00"),
|
||||
culture: "ko-KR",
|
||||
format : "yyyy-MM-dd"
|
||||
});
|
||||
|
|
|
|||
|
|
@ -139,15 +139,18 @@ function goAdminPage(){
|
|||
<li class="mega-menu-dropdown">
|
||||
<a href='<c:url value="board-notice.do"/>'> 공지사항 </a>
|
||||
</li>
|
||||
<li class="mega-menu-dropdown">
|
||||
<a href='<c:url value="board-community.do"/>'> 커뮤니티 </a>
|
||||
</li>
|
||||
<li class="mega-menu-dropdown">
|
||||
<a href='<c:url value="board-qna.do"/>'> Q&A </a>
|
||||
</li>
|
||||
<li class="mega-menu-dropdown">
|
||||
<a href='<c:url value="board-bug.do"/>'> 장애신고 </a>
|
||||
</li>
|
||||
|
||||
<c:if test="${fn:length(sessionVO.sUserId) != 0}">
|
||||
<li class="mega-menu-dropdown">
|
||||
<a href='<c:url value="board-community.do"/>'> 자료실 </a>
|
||||
</li>
|
||||
<li class="mega-menu-dropdown">
|
||||
<a href='<c:url value="board-qna.do"/>'> Q&A </a>
|
||||
</li>
|
||||
<li class="mega-menu-dropdown">
|
||||
<a href='<c:url value="board-bug.do"/>'> 장애신고 </a>
|
||||
</li>
|
||||
</c:if>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
|
@ -1806,7 +1806,12 @@ footer .newsletter input { color: #6f6f6f; letter-spacing: normal; }
|
|||
.table .input {margin: 0px;}
|
||||
|
||||
.table textarea {display: block;}
|
||||
|
||||
.table #dropZone { height: auto !important; position: relative; }
|
||||
.table #dropZone p { padding: 10px; }
|
||||
.table #fileInput { position: absolute; top:0; left: 0; width: 100%; height: 100%; opacity: 0; }
|
||||
.table #dropZone.dragover { background-color: #e9f7fe; border-color: #17a2b8; }
|
||||
.table #dropZone #fileList { margin-bottom: 0}
|
||||
.table .remove-btn { background: url(../img/common/icon/ico_btn_delete_s.png) no-repeat 10% 50%; display: inline-block; padding: 6px 0px 0px 23px; width: 60px; height: 30px; background-color: #a5d0e0; color: #fff; vertical-align: middle; border-radius: 6px;}
|
||||
.table-content-item {min-height: 200px;}
|
||||
.table-top-btn-group {position: relative; width: 100%; display: table; box-sizing: border-box; text-align: right; padding-bottom: 20px;}
|
||||
.table-bottom-btn-group {position: relative; width: 100%; display: table; box-sizing: border-box; text-align: right; padding-top: 20px;}
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 412 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.1 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 791 B |
Binary file not shown.
|
After Width: | Height: | Size: 4.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.5 KiB |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue