사업제휴 페이지 복원

This commit is contained in:
현성필
2025-11-21 17:46:46 +09:00
parent 9497dd98f9
commit 15c872ca7f
8 changed files with 320 additions and 88 deletions
@@ -0,0 +1,61 @@
package com.eactive.apim.portal.apps.community.partnership.controller;
import com.eactive.apim.portal.apps.community.partnership.dto.PartnershipApplicationDTO;
import com.eactive.apim.portal.apps.community.partnership.service.PartnershipApplicationFacade;
import com.eactive.apim.portal.common.util.SecurityUtil;
import java.io.IOException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
@Controller
@RequestMapping("/partnership")
public class PartnershipApplicationController {
private final PartnershipApplicationFacade partnershipApplicationFacade;
@Autowired
public PartnershipApplicationController(PartnershipApplicationFacade partnershipApplicationFacade){
this.partnershipApplicationFacade = partnershipApplicationFacade;
}
@GetMapping
public String newPartnershipApplicationForm(Model model, HttpServletRequest request) {
if (!SecurityUtil.isAuthenticated()) {
return "redirect:/login?redirect=/partnership";
}
String referer = request.getHeader("Referer");
if(referer != null && !referer.contains("/partnership")) {
request.getSession().setAttribute("previousPage", referer);
}
model.addAttribute("partnership", new PartnershipApplicationDTO());
return "apps/community/mainPartnershipForm";
}
@PostMapping
public String createPartnershipApplication(@Valid @ModelAttribute PartnershipApplicationDTO partnershipApplicationDTO,
BindingResult bindingResult, HttpServletRequest httpServletRequest, RedirectAttributes redirectAttributes) throws IOException {
if (bindingResult.hasErrors()) {
return "apps/community/mainPartnershipForm";
}
partnershipApplicationFacade.createPartnershipApplication(partnershipApplicationDTO);
// 성공 메시지 추가
redirectAttributes.addFlashAttribute("success", "사업제휴신청이 완료되었습니다.");
httpServletRequest.getSession().removeAttribute("previousPage");
return "redirect:/partnership";
}
}
@@ -0,0 +1,29 @@
package com.eactive.apim.portal.apps.community.partnership.dto;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.Size;
import org.springframework.web.multipart.MultipartFile;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PartnershipApplicationDTO {
@Size(max = 255, message = "제목은 255자를 초과할 수 없습니다.")
private String bizSubject;
private String bizDetail;
/**
* 첨부파일ID
*/
private String fileId;
private List<MultipartFile> files;
}
@@ -0,0 +1,19 @@
package com.eactive.apim.portal.apps.community.partnership.mapper;
import com.eactive.apim.portal.apps.community.partnership.dto.PartnershipApplicationDTO;
import com.eactive.apim.portal.common.mapper.CommonMapper;
import com.eactive.apim.portal.partnershipapplication.entity.PartnershipApplication;
import org.mapstruct.Mapper;
import org.mapstruct.ReportingPolicy;
import org.springframework.stereotype.Component;
@Mapper(componentModel = "spring",
uses = {CommonMapper.class},
unmappedTargetPolicy = ReportingPolicy.IGNORE)
@Component
public interface PartnershipApplicationMapper {
PartnershipApplication toEntity(PartnershipApplicationDTO dto);
}
@@ -0,0 +1,12 @@
package com.eactive.apim.portal.apps.community.partnership.repository;
import com.eactive.apim.portal.partnershipapplication.entity.PartnershipApplication;
import com.eactive.eai.rms.data.EMSDataSource;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository;
@Repository
@EMSDataSource
public interface PartnershipApplicationRepository extends JpaRepository<PartnershipApplication, String>, JpaSpecificationExecutor<PartnershipApplication> {
}
@@ -0,0 +1,9 @@
package com.eactive.apim.portal.apps.community.partnership.service;
import com.eactive.apim.portal.apps.community.partnership.dto.PartnershipApplicationDTO;
import java.io.IOException;
public interface PartnershipApplicationFacade {
void createPartnershipApplication(PartnershipApplicationDTO partnershipApplicationDTO) throws IOException;
}
@@ -0,0 +1,37 @@
package com.eactive.apim.portal.apps.community.partnership.service;
import com.eactive.apim.portal.apps.community.partnership.dto.PartnershipApplicationDTO;
import com.eactive.apim.portal.apps.community.partnership.mapper.PartnershipApplicationMapper;
import com.eactive.apim.portal.file.entity.FileInfo;
import com.eactive.apim.portal.file.service.FileService;
import com.eactive.apim.portal.file.service.FileTypeContext;
import com.eactive.apim.portal.partnershipapplication.entity.PartnershipApplication;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.io.IOException;
@Service
@RequiredArgsConstructor
public class PartnershipApplicationFacadeImpl implements PartnershipApplicationFacade {
private final PartnershipApplicationService partnershipApplicationService;
private final PartnershipApplicationMapper partnershipApplicationMapper;
private final FileService fileService;
@Override
public void createPartnershipApplication(PartnershipApplicationDTO partnershipApplicationDTO) throws IOException {
FileInfo file = null;
if (!partnershipApplicationDTO.getFiles().isEmpty()) {
FileTypeContext.setFileType("business");
file = fileService.createFile(partnershipApplicationDTO.getFiles());
}
PartnershipApplication partnershipApplication = partnershipApplicationMapper.toEntity(partnershipApplicationDTO);
if (file != null) {
partnershipApplication.setFileId(file.getFileId());
}
partnershipApplicationService.createPartnershipApplication(partnershipApplication);
}
}
@@ -0,0 +1,24 @@
package com.eactive.apim.portal.apps.community.partnership.service;
import com.eactive.apim.portal.apps.community.partnership.repository.PartnershipApplicationRepository;
import com.eactive.apim.portal.partnershipapplication.entity.PartnershipApplication;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@Transactional
public class PartnershipApplicationService {
private final PartnershipApplicationRepository partnershipApplicationRepository;
@Autowired
public PartnershipApplicationService(PartnershipApplicationRepository partnershipApplicationRepository) {
this.partnershipApplicationRepository = partnershipApplicationRepository;
}
public void createPartnershipApplication(PartnershipApplication partnershipApplication) {
partnershipApplicationRepository.save(partnershipApplication);
}
}
@@ -1,77 +1,111 @@
<!doctype html> <!doctype html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout/kbank_base_layout}"> xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout/kjbank_base_layout}">
<body> <body>
<section class="content" layout:fragment="contentFragment"> <section class="content" layout:fragment="contentFragment">
<div class="content_wrap"> <div class="content_wrap">
<div class="sub_title3"> <!-- Page Header -->
<h2 class="title">사업 제휴 신청</h2> <div class="page-header">
</div> <div class="page-header-content">
<div class="inner i_cs h_inner6"> <h1 class="page-title">
<div class="con_title f_tit"> <i class="fas fa-handshake"></i>
<p>Kbank는 온라인 비즈니스 혁신을 위한 <span>사업 제안을 환영</span>합니다.</p> 사업 제휴 신청
</h1>
<p class="page-description">
광주은행은 온라인 비즈니스 혁신을 위한 <span class="highlight">사업 제안을 환영</span>합니다.
</p>
</div> </div>
</div>
<div class="form_type"> <!-- Partnership Form Card -->
<div class="partnership-form-container">
<div class="card partnership-card">
<form name="partnershipForm" id="partnershipForm" th:action="@{/partnership}" th:object="${partnership}" <form name="partnershipForm" id="partnershipForm" th:action="@{/partnership}" th:object="${partnership}"
enctype="multipart/form-data" method="post"> enctype="multipart/form-data" method="post">
<div class="info1">
<p class="title w_tit">
<span class="dot">사업 제목</span>
</p>
<div class="info_line info_add">
<div class="info_box1 w_inp4">
<input type="text" class="common_input_type_1" id="bizSubject" th:field="*{bizSubject}"
placeholder="제안하고자 하시는 사업제목" required>
</div>
</div>
<div class="invalid-feedback" th:if="${#fields.hasErrors('bizSubject')}"
th:errors="*{bizSubject}"></div>
</div>
<div class="info1"> <!-- 사업 제목 -->
<p class="title w_tit"> <div class="form-group">
<span class="dot">사업 내용</span> <label for="bizSubject">
</p> <i class="fas fa-lightbulb"></i>
<div class="info_line info_add"> 사업 제목
<div class="info_box1 qna_text w_inp4"> <span class="required">*</span>
<textarea class="common_textareaType_1 f_inp" id="bizDetail" th:field="*{bizDetail}" </label>
placeholder="제안하고자 하시는 사업내용" required></textarea> <input type="text"
</div> class="form-control"
</div> id="bizSubject"
<div class="invalid-feedback" th:if="${#fields.hasErrors('bizDetail')}" th:field="*{bizSubject}"
th:errors="*{bizDetail}"></div> placeholder="제안하고자 하시는 사업 제목을 입력해 주세요"
</div> required>
<div class="form-error" th:if="${#fields.hasErrors('bizSubject')}" th:errors="*{bizSubject}">
<div class="info1"> <i class="fas fa-exclamation-circle"></i>
<p class="title tit_add w_tit">첨부파일</p> <span></span>
<div class="info_line info_add">
<div class="info_box1 info_add2">
<div class="filebox">
<input class="upload-name" value="첨부파일을 등록 해 주세요" disabled="disabled">
</div>
<div class="btn_check">
<div class="file_btn">
<label for="file">파일선택</label>
<input type="file" id="file" name="files" accept=".pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.hwp,.gif,.jpg,.png">
</div>
</div>
</div>
<div class="list-dot">
<p>첨부파일은 10MB 이내로 등록해 주세요.</p>
<p>문서파일과 이미지 파일만 등록 가능합니다. (pdf, doc, docx, xls, xlsx, ppt, pptx, hwp, gif, jpg,
png)</p>
</div>
</div> </div>
</div> </div>
<div class="btn_application m_top"> <!-- 사업 내용 -->
<a th:href="@{/}" id="cancelButton" class="common_btn_type_1 gray"><span>취소</span></a> <div class="form-group">
<div class="btn_gap"></div> <label for="bizDetail">
<button type="button" class="common_btn_type_1 btn_register"><span>제휴신청</span></button> <i class="fas fa-file-alt"></i>
사업 내용
<span class="required">*</span>
</label>
<textarea class="form-control"
id="bizDetail"
th:field="*{bizDetail}"
rows="8"
placeholder="제안하고자 하시는 사업 내용을 상세히 입력해 주세요"
required></textarea>
<div class="form-hint">
<i class="fas fa-info-circle"></i>
사업의 목적, 내용, 기대 효과 등을 구체적으로 작성해 주세요
</div>
<div class="form-error" th:if="${#fields.hasErrors('bizDetail')}" th:errors="*{bizDetail}">
<i class="fas fa-exclamation-circle"></i>
<span></span>
</div>
</div>
<!-- 첨부파일 -->
<div class="form-group">
<label>
<i class="fas fa-paperclip"></i>
첨부파일
</label>
<div class="file-upload-wrapper">
<input type="text"
class="file-name-display"
value="첨부파일을 등록해 주세요"
readonly>
<label for="file" class="btn btn-primary">
<i class="fas fa-upload"></i>
파일 선택
</label>
<button type="button" class="file-remove-btn" style="display: none;">
<i class="fas fa-times"></i>
</button>
<input type="file"
id="file"
name="files"
accept=".pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.hwp,.gif,.jpg,.png">
</div>
<div class="form-help-text">
<p><i class="fas fa-check-circle"></i> 첨부파일은 10MB 이내로 등록해 주세요</p>
<p><i class="fas fa-check-circle"></i> 문서파일과 이미지 파일만 등록 가능합니다 (pdf, doc, docx, xls, xlsx, ppt, pptx, hwp, gif, jpg, png)</p>
</div>
</div>
<!-- 버튼 영역 -->
<div class="form-actions">
<a th:href="@{/}" id="cancelButton" class="action-btn-secondary">
<i class="fas fa-times"></i>
취소
</a>
<button type="button" class="btn btn-primary btn_register">
<i class="fas fa-paper-plane"></i>
제휴 신청
</button>
</div> </div>
</form> </form>
</div> </div>
@@ -97,17 +131,21 @@
</script> </script>
<script th:inline="javascript"> <script th:inline="javascript">
$(function () { $(function () {
let fnRegister = document.querySelector('.btn_register'); const fnRegister = document.querySelector('.btn_register');
let cancelButton = document.getElementById('cancelButton'); const fileInput = document.getElementById('file');
const fileNameInput = document.querySelector('.file-name-display');
const fileRemoveBtn = document.querySelector('.file-remove-btn');
const allowFileExtention = ['pdf','doc','docx','xls','xlsx','ppt','pptx','hwp','gif','jpg','png'];
// Form submission
fnRegister.addEventListener('click', function (event) { fnRegister.addEventListener('click', function (event) {
event.preventDefault(); event.preventDefault();
let form = document.getElementById('partnershipForm'); const form = document.getElementById('partnershipForm');
let bizSubject = document.getElementById('bizSubject').value.trim(); const bizSubject = document.getElementById('bizSubject').value.trim();
let bizDetail = document.getElementById('bizDetail').value.trim(); const bizDetail = document.getElementById('bizDetail').value.trim();
// validation 체크 // Validation
if (!bizSubject) { if (!bizSubject) {
customPopups.showAlert('사업 제목을 작성해 주세요.'); customPopups.showAlert('사업 제목을 작성해 주세요.');
document.getElementById('bizSubject').focus(); document.getElementById('bizSubject').focus();
@@ -123,51 +161,54 @@
form.submit(); form.submit();
}); });
document.getElementById('partnershipForm').bizSubject.focus(); // Auto focus on first field
const fileInput = document.getElementById('file'); document.getElementById('bizSubject').focus();
const fileNameInput = document.querySelector('.upload-name');
const allowFileExtention = ['pdf','doc','docx','xls','xlsx','ppt','pptx','hwp','gif','jpg','png'];
// File validation functions
function isValidFileExtention(filename) { function isValidFileExtention(filename) {
const extention = filename.split('.').pop().toLowerCase(); const extention = filename.split('.').pop().toLowerCase();
return allowFileExtention.includes(extention); return allowFileExtention.includes(extention);
} }
function isValidFileSize(filesize) { function isValidFileSize(filesize) {
const maxSize = 10 * 1024 * 1024; const maxSize = 10 * 1024 * 1024; // 10MB
return filesize <= maxSize; return filesize <= maxSize;
} }
// File input change event
fileInput.addEventListener('change', function () { fileInput.addEventListener('change', function () {
const file = fileInput.files[0]; const file = fileInput.files[0];
if (file) { if (file) {
if(!isValidFileExtention(file.name)) { // Validate file extension
const errorMsg = '허용된 파일 형식이 아닙니다. <br>문서파일과 이미지 파일만 등록 가능합니다. <br> (pdf, doc, docx, xls, xlsx, ppt, pptx, hwp, gif, jpg, png)'; if (!isValidFileExtention(file.name)) {
const formattedMsg = errorMsg.replace(/\n/g,'<br>'); const errorMsg = '허용된 파일 형식이 아닙니다.<br>문서파일과 이미지 파일만 등록 가능합니다.<br>(pdf, doc, docx, xls, xlsx, ppt, pptx, hwp, gif, jpg, png)';
$('#customAlertMessage').html(formattedMsg); $('#customAlertMessage').html(errorMsg);
$('#customAlert').css('display','flex'); $('#customAlert').css('display','flex');
fileInput.value = '';
return; return;
} }
if(!isValidFileSize(file.size)) {
customPopups.showAlert('첨부파일은 10MB 이내로 등록해 주세요.') // Validate file size
if (!isValidFileSize(file.size)) {
customPopups.showAlert('첨부파일은 10MB 이내로 등록해 주세요.');
fileInput.value = '';
return; return;
} }
// Update UI
fileNameInput.value = file.name; fileNameInput.value = file.name;
fileNameInput.classList.remove('before'); fileNameInput.classList.add('has-file');
fileNameInput.classList.add('i_trash', 'after'); fileRemoveBtn.style.display = 'inline-flex';
} }
}); });
fileNameInput.addEventListener('click', function () { // File remove button click event
if (fileNameInput.classList.contains('i_trash')) { fileRemoveBtn.addEventListener('click', function () {
fileInput.value = ''; fileInput.value = '';
fileNameInput.value = ''; fileNameInput.value = '첨부파일을 등록해 주세요';
fileNameInput.classList.remove('i_trash', 'after'); fileNameInput.classList.remove('has-file');
fileNameInput.classList.add('before'); fileRemoveBtn.style.display = 'none';
}
}); });
}); });
</script> </script>