사업제휴 페이지 복원

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>
<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>
<section class="content" layout:fragment="contentFragment">
<div class="content_wrap">
<div class="sub_title3">
<h2 class="title">사업 제휴 신청</h2>
</div>
<div class="inner i_cs h_inner6">
<div class="con_title f_tit">
<p>Kbank는 온라인 비즈니스 혁신을 위한 <span>사업 제안을 환영</span>합니다.</p>
<!-- Page Header -->
<div class="page-header">
<div class="page-header-content">
<h1 class="page-title">
<i class="fas fa-handshake"></i>
사업 제휴 신청
</h1>
<p class="page-description">
광주은행은 온라인 비즈니스 혁신을 위한 <span class="highlight">사업 제안을 환영</span>합니다.
</p>
</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}"
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">
<span class="dot">사업 내용</span>
</p>
<div class="info_line info_add">
<div class="info_box1 qna_text w_inp4">
<textarea class="common_textareaType_1 f_inp" id="bizDetail" th:field="*{bizDetail}"
placeholder="제안하고자 하시는 사업내용" required></textarea>
</div>
</div>
<div class="invalid-feedback" th:if="${#fields.hasErrors('bizDetail')}"
th:errors="*{bizDetail}"></div>
</div>
<div class="info1">
<p class="title tit_add w_tit">첨부파일</p>
<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 class="form-group">
<label for="bizSubject">
<i class="fas fa-lightbulb"></i>
사업 제목
<span class="required">*</span>
</label>
<input type="text"
class="form-control"
id="bizSubject"
th:field="*{bizSubject}"
placeholder="제안하고자 하시는 사업 제목을 입력해 주세요"
required>
<div class="form-error" th:if="${#fields.hasErrors('bizSubject')}" th:errors="*{bizSubject}">
<i class="fas fa-exclamation-circle"></i>
<span></span>
</div>
</div>
<div class="btn_application m_top">
<a th:href="@{/}" id="cancelButton" class="common_btn_type_1 gray"><span>취소</span></a>
<div class="btn_gap"></div>
<button type="button" class="common_btn_type_1 btn_register"><span>제휴신청</span></button>
<!-- 사업 내용 -->
<div class="form-group">
<label for="bizDetail">
<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>
</form>
</div>
@@ -97,17 +131,21 @@
</script>
<script th:inline="javascript">
$(function () {
let fnRegister = document.querySelector('.btn_register');
let cancelButton = document.getElementById('cancelButton');
const fnRegister = document.querySelector('.btn_register');
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) {
event.preventDefault();
let form = document.getElementById('partnershipForm');
let bizSubject = document.getElementById('bizSubject').value.trim();
let bizDetail = document.getElementById('bizDetail').value.trim();
const form = document.getElementById('partnershipForm');
const bizSubject = document.getElementById('bizSubject').value.trim();
const bizDetail = document.getElementById('bizDetail').value.trim();
// validation 체크
// Validation
if (!bizSubject) {
customPopups.showAlert('사업 제목을 작성해 주세요.');
document.getElementById('bizSubject').focus();
@@ -123,51 +161,54 @@
form.submit();
});
document.getElementById('partnershipForm').bizSubject.focus();
const fileInput = document.getElementById('file');
const fileNameInput = document.querySelector('.upload-name');
const allowFileExtention = ['pdf','doc','docx','xls','xlsx','ppt','pptx','hwp','gif','jpg','png'];
// Auto focus on first field
document.getElementById('bizSubject').focus();
// File validation functions
function isValidFileExtention(filename) {
const extention = filename.split('.').pop().toLowerCase();
return allowFileExtention.includes(extention);
}
function isValidFileSize(filesize) {
const maxSize = 10 * 1024 * 1024;
const maxSize = 10 * 1024 * 1024; // 10MB
return filesize <= maxSize;
}
// File input change event
fileInput.addEventListener('change', function () {
const file = fileInput.files[0];
if (file) {
if(!isValidFileExtention(file.name)) {
const errorMsg = '허용된 파일 형식이 아닙니다. <br>문서파일과 이미지 파일만 등록 가능합니다. <br> (pdf, doc, docx, xls, xlsx, ppt, pptx, hwp, gif, jpg, png)';
const formattedMsg = errorMsg.replace(/\n/g,'<br>');
$('#customAlertMessage').html(formattedMsg);
// Validate file extension
if (!isValidFileExtention(file.name)) {
const errorMsg = '허용된 파일 형식이 아닙니다.<br>문서파일과 이미지 파일만 등록 가능합니다.<br>(pdf, doc, docx, xls, xlsx, ppt, pptx, hwp, gif, jpg, png)';
$('#customAlertMessage').html(errorMsg);
$('#customAlert').css('display','flex');
fileInput.value = '';
return;
}
if(!isValidFileSize(file.size)) {
customPopups.showAlert('첨부파일은 10MB 이내로 등록해 주세요.')
// Validate file size
if (!isValidFileSize(file.size)) {
customPopups.showAlert('첨부파일은 10MB 이내로 등록해 주세요.');
fileInput.value = '';
return;
}
// Update UI
fileNameInput.value = file.name;
fileNameInput.classList.remove('before');
fileNameInput.classList.add('i_trash', 'after');
fileNameInput.classList.add('has-file');
fileRemoveBtn.style.display = 'inline-flex';
}
});
fileNameInput.addEventListener('click', function () {
if (fileNameInput.classList.contains('i_trash')) {
fileInput.value = '';
fileNameInput.value = '';
fileNameInput.classList.remove('i_trash', 'after');
fileNameInput.classList.add('before');
}
// File remove button click event
fileRemoveBtn.addEventListener('click', function () {
fileInput.value = '';
fileNameInput.value = '첨부파일을 등록해 주세요';
fileNameInput.classList.remove('has-file');
fileRemoveBtn.style.display = 'none';
});
});
</script>