Compare commits

..

2 Commits

8 changed files with 364 additions and 14 deletions

View File

@ -22,7 +22,29 @@ import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import egovframework.com.cmm.service.EgovProperties;
public class CryptoUtil {
// ===== [고정 키 AES - 빠른 버전] =====
private static SecretKeySpec FIXED_SECRET_KEY;
/**
* encryptQuickAES key
* 1
*/
static {
try {
String key = EgovProperties.getProperty("SHA256.secret_key").trim();
MessageDigest sha = MessageDigest.getInstance("SHA-256");
byte[] keyBytes = sha.digest(key.getBytes("UTF-8"));
FIXED_SECRET_KEY = new SecretKeySpec(keyBytes, "AES");
} catch (Exception e) {
throw new RuntimeException("CryptoUtil AES key initialization failed", e);
}
}
/**
* MD5 .
*
@ -142,4 +164,27 @@ public class CryptoUtil {
byte[] decryptedTextBytes = cipher.doFinal(encryoptedTextBytes);
return new String(decryptedTextBytes);
}
/**
* AES ()
*/
public static String encryptQuickAES(String plainText) throws Exception {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, FIXED_SECRET_KEY);
return Base64.getEncoder()
.encodeToString(cipher.doFinal(plainText.getBytes("UTF-8")));
}
/**
* AES
*/
public static String decryptQuickAES(String cipherText) throws Exception {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, FIXED_SECRET_KEY);
return new String(
cipher.doFinal(Base64.getDecoder().decode(cipherText)),
"UTF-8"
);
}
}

View File

@ -169,6 +169,76 @@ public class DrillingInputController {
return jSONOResponse;
}
// 발주기관 건설사목록 가져오기
@ResponseBody
@RequestMapping(value = "/drilling-company-list", method = RequestMethod.GET, produces = "application/json; charset=UTF-8")
public String getDrillingCompanyList(HttpServletRequest request, HttpServletResponse response, @RequestParam HashMap<String,Object> params) throws Exception {
JSONObject jsonObject = new JSONObject();
strUtil sUtil = new strUtil();
String projectName = sUtil.checkNull((String)params.get("companyName"));
JSONArray jsonListObject = new JSONArray();
if( projectName == ""){
jsonObject.put("resultMessage", "OK");
jsonObject.put("resultCode", 200);
jsonObject.put("result", new JSONObject().put("list", jsonListObject));
} else {
JSONObject result = new JSONObject();
result.put("list", drillingInputService.selectConstructCompanyList(params));
jsonObject.put("resultMessage", "OK");
jsonObject.put("resultCode", 200);
jsonObject.put("result", result);
}
response.setContentType("application/json; charset=UTF-8"); // 응답 헤더 설정
response.setCharacterEncoding("UTF-8"); // 응답 데이터 인코딩 설정 (중요)
try (OutputStream os = response.getOutputStream()) { // OutputStream 사용
os.write(jsonObject.toString().getBytes("UTF-8")); // UTF-8 인코딩하여 출력
}
return null; // @ResponseBody이므로 반환 값은 필요 없습니다.
}
// [발주기관 > 건설현장입력] 건설사 계정 선택시 이름, 연락처 자동셋팅
@ResponseBody
@RequestMapping(value = "/const-user-info", method = RequestMethod.GET, produces = "application/json; charset=UTF-8")
public String getConstUserInfo(HttpServletRequest request, HttpServletResponse response, @RequestParam HashMap<String,Object> params) throws Exception {
JSONObject jsonObject = new JSONObject();
strUtil sUtil = new strUtil();
String userid = sUtil.checkNull((String)params.get("encUserid"));
JSONArray jsonListObject = new JSONArray();
if( userid == ""){
jsonObject.put("resultMessage", "OK");
jsonObject.put("resultCode", 200);
jsonObject.put("result", new JSONObject().put("data", jsonListObject));
} else {
JSONObject result = new JSONObject();
result.put("data", drillingInputService.selectConstructUserInfo(params));
jsonObject.put("resultMessage", "OK");
jsonObject.put("resultCode", 200);
jsonObject.put("result", result);
}
response.setContentType("application/json; charset=UTF-8"); // 응답 헤더 설정
response.setCharacterEncoding("UTF-8"); // 응답 데이터 인코딩 설정 (중요)
try (OutputStream os = response.getOutputStream()) { // OutputStream 사용
os.write(jsonObject.toString().getBytes("UTF-8")); // UTF-8 인코딩하여 출력
}
return null; // @ResponseBody이므로 반환 값은 필요 없습니다.
}
/**
* .
* @param request

View File

@ -15,6 +15,7 @@ public interface DrillingInputMapper {
public void spGetMasterCompanyDistrict(HashMap<String, Object> spGetMasterCompanyDistrictParams) throws SQLException;
public List<EgovMap> selectConstructCompanyList(HashMap<String, Object> params) throws SQLException;
public EgovMap getDepartments(HashMap<String, Object> params) throws SQLException;
public HashMap<String, Object> spUdtTblCsi(HashMap<String, Object> params) throws SQLException;

View File

@ -1,15 +1,22 @@
package geoinfo.drilling.input.service;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.json.simple.JSONObject;
import egovframework.rte.psl.dataaccess.util.EgovMap;
public interface DrillingInputService {
HashMap<String, Object> getOrganizationUserGlGmGsGfCodes(String userId) throws Exception;
HashMap<String, Object> drillingInputAdd(HttpServletRequest request, HashMap<String, Object> params) throws Exception;
List<EgovMap> selectConstructCompanyList(HashMap<String, Object> params) throws Exception;
Map<String, Object> selectConstructUserInfo(HashMap<String, Object> params) throws Exception;
void getDepartments(HttpServletRequest request, HashMap<String, Object> params, JSONObject jsonResponse) throws Exception;
HashMap<String, Object> updateProjectCodeAndProjectStateCodeByCid(HttpServletRequest request, HashMap<String, Object> params) throws Exception;
HashMap<String, Object> updateProjectCodeAndProjectStateCodeByProjectCode(HttpServletRequest request, HashMap<String, Object> params) throws Exception;

View File

@ -1,26 +1,26 @@
package geoinfo.drilling.input.service.impl;
import geoinfo.drilling.input.service.DrillingInputMapper;
import geoinfo.drilling.input.service.DrillingInputService;
import geoinfo.drilling.inquiry.service.DrillingInquiryService;
import geoinfo.main.login.service.LoginMapper;
import geoinfo.util.MyUtil;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import egovframework.com.cmm.service.EgovProperties;
import egovframework.rte.psl.dataaccess.util.EgovMap;
import geoinfo.com.CryptoUtil;
import geoinfo.drilling.input.service.DrillingInputMapper;
import geoinfo.drilling.input.service.DrillingInputService;
import geoinfo.drilling.inquiry.service.DrillingInquiryService;
import geoinfo.main.login.service.LoginMapper;
import geoinfo.util.MyUtil;
@Service("drillingInputService")
public class DrillingInputServiceImpl implements DrillingInputService {
@ -76,6 +76,11 @@ public class DrillingInputServiceImpl implements DrillingInputService {
params.put("masterCompanyThCode", MyUtil.getStringFromObject( spGetMasterCompanyDistrictParams.get("v_gs") ));
params.put("masterCompanyName", MyUtil.getStringFromObject( spGetMasterCompanyDistrictParams.get("v_gf") ));
// 건설사 계정 연결(암호화된 userid 복호화)
String encryptId = MyUtil.getStringFromObject( params.get("encUserid") );
String decryptId = CryptoUtil.decryptQuickAES(encryptId);
params.put("constUserid", decryptId);
try {
@ -89,6 +94,49 @@ public class DrillingInputServiceImpl implements DrillingInputService {
}
@Override
public List<EgovMap> selectConstructCompanyList(HashMap<String, Object> params) throws Exception {
List<EgovMap> list = new ArrayList<EgovMap>();
list = drillingInputMapper.selectConstructCompanyList(params);
for (EgovMap map : list) {
String userid = (String) map.get("userid");
if (userid == null) continue;
// 암호화
// String encryptId = CryptoUtil.encryptAES256(userid, secret_key);
String encryptId = CryptoUtil.encryptQuickAES(userid);
// userid 제거
map.remove("userid");
// 복호화 (검증 or 필요 시)
// String decryptId = CryptoUtil.decryptAES256(encryptId, secret_key);
map.put("encryptId", encryptId);
// map.put("decryptId", decryptId);
}
return list;
}
@Override
public Map<String, Object> selectConstructUserInfo(HashMap<String, Object> params) throws Exception {
Map<String, Object> result = new HashMap<String, Object>();
// 건설사 계정 연결(암호화된 userid 복호화)
String encryptId = MyUtil.getStringFromObject( params.get("encUserid") );
String decryptId = CryptoUtil.decryptQuickAES(encryptId);
params.put("userid", decryptId);
Map<String, Object> infoData = loginMapper.selectWebMemberIn(params);
if (result != infoData) {
result.put("phone", infoData.get("phone"));
result.put("userName", infoData.get("user_name"));
}
return result;
}
@Override
public void getDepartments(HttpServletRequest request, HashMap<String, Object> params, JSONObject jsonResponse) throws Exception {
@ -253,6 +301,11 @@ public class DrillingInputServiceImpl implements DrillingInputService {
findConstCompanyCodeByConstCompanyNameParams.put("masterCompanyThCode", MyUtil.getStringFromObject( findConstCompanyCodeByConstCompanyNameParams.get("v_gs") ));
findConstCompanyCodeByConstCompanyNameParams.put("masterCompanyName", MyUtil.getStringFromObject( findConstCompanyCodeByConstCompanyNameParams.get("v_gf") ));
// 건설사 계정 연결(암호화된 userid 복호화)
String encryptId = MyUtil.getStringFromObject( params.get("encUserid") );
String decryptId = CryptoUtil.decryptQuickAES(encryptId);
params.put("constUserid", decryptId);
try {

View File

@ -38,3 +38,8 @@ JWT.secret_key=RnrxhWlQksportalSystem!@!@$#@!@#@!$12442321
JWT.access_expired=1800000
O2MAP.wms.url=http://10.dbnt.co.kr:2936/o2map/services/wms
LOCAL.wms.url=http://10.dbnt.co.kr:2936/o2map/services/wms
###############################################
################### SHA256 #################
###############################################
SHA256.secret_key=RnrxhwlQksportalSystem!@34$%

View File

@ -34,6 +34,7 @@
#{constCompanyTel},
'C',
#{userId},
#{constUserid},
#{v_CID, mode=OUT, jdbcType=INTEGER},
#{v_RetCode, mode=OUT, jdbcType=INTEGER},
#{v_RetMsg, mode=OUT, jdbcType=VARCHAR}
@ -61,6 +62,23 @@
) }
</select>
<select id="selectConstructCompanyList" parameterType="map" resultType="egovMap">
SELECT
TRIM(wmi.COMPANY_NAME) AS COMPANY_NAME,
wmi.USERID,
SUBSTR(wmi.USERID, 1, 2) || '****' || SUBSTR(wmi.USERID, LENGTH(wmi.USERID)-1, 2) AS MaskedID,
TRIM(wmi.NEW_ADDRESS) AS NEW_ADDRESS,
TRIM(wmi.PART_NAME) AS PART_NAME,
wmi.COMPANY_REGIST_NO
FROM
web_member_in wmi
WHERE
wmi.CLS = 1
and wmi.COMPANY_NAME NOT IN ('111', 'a')
and wmi.COMPANY_NAME is not NULL
and wmi.COMPANY_NAME LIKE '%' || #{companyName} || '%'
</select>
<select id="getDepartments" parameterType="map" resultType="egovMap">
SELECT
(SELECT tgd.GL_DISTRICT
@ -101,6 +119,7 @@
#{constCompanyAdmin},
#{constCompanyTel},
#{userId},
#{constUserid},
#{v_RetCode, mode=OUT, jdbcType=INTEGER},
#{v_RetMsg, mode=OUT, jdbcType=VARCHAR}
) }

View File

@ -35,6 +35,36 @@ if (request.getSession().getAttribute("CLS") == null || "2".equals(request.getSe
<c:import url="/drilling/common/includeTopMenu.do" charEncoding="UTF-8" />
<!-- header end-->
<style>
.table-scrollable {
overflow: visible;
}
.info-row {
position: relative;
}
.suggestionList {
border: 1px solid #ccc;
width: 300px; /* 입력창 너비에 맞춰 조절 */
height: 160px;
overflow-y: scroll;
position: absolute;
top: 39px;
left: 6px;
background-color: white;
display: none;
z-index: 3;
}
.suggestionList div {
padding: 5px;
cursor: pointer;
}
.suggestionList div:hover {
background-color: #f0f0f0;
}
.suggestionList div .organizational-structure {
color: red;
}
</style>
<iframe class="iframe-body-none" name="hiddenFrame"></iframe>
@ -87,6 +117,7 @@ if (request.getSession().getAttribute("CLS") == null || "2".equals(request.getSe
                        <button class="btn-green btn-excel-download" type="button" id="excel-download-btn"
onclick="javascript:fileDownload('발주기관_건설현장입력_양식.xls', 'drilling_construction_input_form.xls')">엑셀 양식 다운로드</button>
</div>
<div id="suggestionList"></div>
<div id="table-container">
</div>
<div class="bottom-buttons">
@ -229,13 +260,15 @@ if (request.getSession().getAttribute("CLS") == null || "2".equals(request.getSe
</tr>
<tr>
<th>건설사</th>
<td colspan="3">
<input type="text" value="" class="input-box information1" id="const-company-dept-` + tableId + `" placeholder="건설사명">
<td colspan="3" class="info-row">
<input type="hidden" value="" id="const-user-id-` + tableId + `" name="const-user-id" />
<input type="text" value="" class="input-box information1 const-company-dept" id="const-company-dept-` + tableId + `" placeholder="건설사명">
<input type="text" value="" class="input-box information2" id="const-company-admin-` + tableId + `" placeholder="담당자">
<input type="text" value="010-0000-0000" class="input-box information3" id="const-company-tel-` + tableId + `" placeholder="담당자 연락처">
<label class="check-box unselected-constructor-label" for="unselected-constructor-` + tableId + `"><input type="checkbox" id="unselected-constructor-` + tableId + `">
<span class="unselected-constructor-label-text">건설사 미선정</span>
</label>
<div style="display: none;" class="suggestionList"></div>
</td>
</tr>`;
if (tableId > 1) {
@ -338,6 +371,99 @@ if (request.getSession().getAttribute("CLS") == null || "2".equals(request.getSe
});
document.querySelectorAll('.const-company-dept').forEach(function (companyNameInput) {
document.addEventListener('keyup', function (e) {
if (!e.target.classList.contains('const-company-dept')) return;
const companyNameInput = e.target;
const companyName = companyNameInput.value.trim();
const suggestionListDiv = companyNameInput
.closest('td')
.querySelector('.suggestionList');
const hiddenUserIdInput = companyNameInput
.closest('td')
.querySelector('input[type="hidden"][name="const-user-id"]');
const tableContainerDiv = companyNameInput
.closest('div')
if (!suggestionListDiv) return;
if (companyName.length === 0) {
suggestionListDiv.style.display = 'none';
return;
}
$.ajax({
type: 'GET',
url: '/drilling-company-list.json',
dataType: 'json',
data: {
companyName: companyName,
isProjectNameChecking: 'true'
},
success: function (json) {
suggestionListDiv.innerHTML = '';
suggestionListDiv.style.display = 'none';
const list = json?.result?.list || [];
if (list.length === 0) return;
list.forEach(function (item) {
const suggestionItem = document.createElement('div');
let maskedid = '';
let partName = '';
let newAddress = '';
if (item.maskedid) maskedid = item.maskedid;
if (item.partName) partName = item.partName;
if (item.newAddress) newAddress = item.newAddress;
const keyword = companyName;
const regex = new RegExp(keyword, 'gi');
const boldCompanyName = item.companyName.replace(
regex,
'<b style="background:yellow">' + keyword + '</b>'
);
suggestionItem.innerHTML =
'<span class="organizational-structure">' + boldCompanyName + '</span><br />' +
'<span>' + maskedid + '(' + partName + ')' + '/' + newAddress
'</span>';
suggestionItem.addEventListener('click', function () {
companyNameInput.value = item.companyName;
hiddenUserIdInput.value = item.encryptId;
suggestionListDiv.style.display = 'none';
setConstInfo(item.encryptId, tableContainerDiv); // 건설사 계정 선택시 이름, 연락처 자동 셋팅
});
suggestionListDiv.appendChild(suggestionItem);
});
const rect = companyNameInput.getBoundingClientRect();
suggestionListDiv.style.display = 'block';
}
});
});
});
// 사용자가 추천 목록 외부를 클릭하면 목록 숨기기 (선택적)
/* document.onclick = function(event) {
if (event.target !== companyNameInput && event.target !== suggestionListDiv && !suggestionListDiv.contains(event.target)) {
suggestionListDiv.style.display = "none";
}
};*/
document.addEventListener('click', function (e) {
document.querySelectorAll('.suggestionList').forEach(function (list) {
if (list.contains(e.target)) return;
const input = list.closest('td')?.querySelector('.const-company-dept');
if (input && input === e.target) return;
list.style.display = 'none';
});
});
});
// 사업 등록 처리
@ -390,6 +516,7 @@ if (request.getSession().getAttribute("CLS") == null || "2".equals(request.getSe
var constCompanyNameEle = document.getElementById('const-company-dept-' + dataIndexValue);
if (constCompanyNameEle) {
jsonItem.constCompanyName = constCompanyNameEle.value;
jsonItem.constCompanyName = constCompanyNameEle.value;
}
// 건설사 - 담당자
var constCompanyAdminEle = document.getElementById('const-company-admin-' + dataIndexValue);
@ -402,6 +529,9 @@ if (request.getSession().getAttribute("CLS") == null || "2".equals(request.getSe
jsonItem.constCompanyTel = constCompanyTelEle.value;
}
// 건설사 - 선택한 건설사 userid
var hiddenUseridEle = document.getElementById('const-user-id-' + dataIndexValue);
jsonItem.encUserid = hiddenUseridEle.value;
if (cid) {
jsonItem.cid=cid;
}
@ -571,6 +701,24 @@ if (request.getSession().getAttribute("CLS") == null || "2".equals(request.getSe
        xhr.send();
    }
function setConstInfo(encId, tblIdx) {
$.ajax({
type: 'GET',
url: '/const-user-info.json',
dataType: 'json',
data: {
encUserid: encId
},
success: function (json) {
// console.log(json)
let data = json.result.data
tblIdx.querySelector('input[id*=const-company-admin-]').value = data.userName;
tblIdx.querySelector('input[id*=const-company-tel-]').value = data.phone;
}
});
}
function triggerExcelUpload() {
// 숨겨진 파일 선택 input을 클릭시킴
document.getElementById('excel-file-input').click();
@ -704,13 +852,15 @@ if (request.getSession().getAttribute("CLS") == null || "2".equals(request.getSe
</tr>
<tr>
<th>건설사</th>
<td colspan="3">
<input type="text" value="` + const_company_dept + `" class="input-box information1" id="const-company-dept-` + tableId + `" placeholder="건설사명">
<td colspan="3" class="info-row">
<input type="hidden" value="" id="const-user-id-` + tableId + `" name="const-user-id" />
<input type="text" value="` + const_company_dept + `" class="input-box information1 const-company-dept" id="const-company-dept-` + tableId + `" placeholder="건설사명">
<input type="text" value="` + const_company_name + `" class="input-box information2" id="const-company-admin-` + tableId + `" placeholder="담당자">
<input type="text" value="` + const_company_tel + `" class="input-box information3" id="const-company-tel-` + tableId + `" placeholder="담당자 연락처">
<label class="check-box unselected-constructor-label" for="unselected-constructor-` + tableId + `"><input type="checkbox" id="unselected-constructor-` + tableId + `" ` + fin_const_company + `>
<span class="unselected-constructor-label-text">건설사 미선정</span>
</label>
<div style="display: none;" class="suggestionList"></div>
</td>
</tr>
<tr>