diff --git a/app/Config/Constants.php b/app/Config/Constants.php
index fb56bb1..bf093d1 100644
--- a/app/Config/Constants.php
+++ b/app/Config/Constants.php
@@ -35,11 +35,11 @@ defined('COMPOSER_PATH') || define('COMPOSER_PATH', ROOTPATH . 'vendor/autoload.
*/
defined('SECOND') || define('SECOND', 1);
defined('MINUTE') || define('MINUTE', 60);
-defined('HOUR') || define('HOUR', 3600);
-defined('DAY') || define('DAY', 86400);
-defined('WEEK') || define('WEEK', 604800);
-defined('MONTH') || define('MONTH', 2_592_000);
-defined('YEAR') || define('YEAR', 31_536_000);
+defined('HOUR') || define('HOUR', 3600);
+defined('DAY') || define('DAY', 86400);
+defined('WEEK') || define('WEEK', 604800);
+defined('MONTH') || define('MONTH', 2_592_000);
+defined('YEAR') || define('YEAR', 31_536_000);
defined('DECADE') || define('DECADE', 315_360_000);
/*
@@ -67,13 +67,23 @@ defined('DECADE') || define('DECADE', 315_360_000);
| http://tldp.org/LDP/abs/html/exitcodes.html
|
*/
-defined('EXIT_SUCCESS') || define('EXIT_SUCCESS', 0); // no errors
-defined('EXIT_ERROR') || define('EXIT_ERROR', 1); // generic error
-defined('EXIT_CONFIG') || define('EXIT_CONFIG', 3); // configuration error
-defined('EXIT_UNKNOWN_FILE') || define('EXIT_UNKNOWN_FILE', 4); // file not found
-defined('EXIT_UNKNOWN_CLASS') || define('EXIT_UNKNOWN_CLASS', 5); // unknown class
+defined('EXIT_SUCCESS') || define('EXIT_SUCCESS', 0); // no errors
+defined('EXIT_ERROR') || define('EXIT_ERROR', 1); // generic error
+defined('EXIT_CONFIG') || define('EXIT_CONFIG', 3); // configuration error
+defined('EXIT_UNKNOWN_FILE') || define('EXIT_UNKNOWN_FILE', 4); // file not found
+defined('EXIT_UNKNOWN_CLASS') || define('EXIT_UNKNOWN_CLASS', 5); // unknown class
defined('EXIT_UNKNOWN_METHOD') || define('EXIT_UNKNOWN_METHOD', 6); // unknown class member
-defined('EXIT_USER_INPUT') || define('EXIT_USER_INPUT', 7); // invalid user input
-defined('EXIT_DATABASE') || define('EXIT_DATABASE', 8); // database error
-defined('EXIT__AUTO_MIN') || define('EXIT__AUTO_MIN', 9); // lowest automatically-assigned error code
-defined('EXIT__AUTO_MAX') || define('EXIT__AUTO_MAX', 125); // highest automatically-assigned error code
+defined('EXIT_USER_INPUT') || define('EXIT_USER_INPUT', 7); // invalid user input
+defined('EXIT_DATABASE') || define('EXIT_DATABASE', 8); // database error
+defined('EXIT__AUTO_MIN') || define('EXIT__AUTO_MIN', 9); // lowest automatically-assigned error code
+defined('EXIT__AUTO_MAX') || define('EXIT__AUTO_MAX', 125); // highest automatically-assigned error code
+
+
+/**
+ * ncloud url
+ */
+define('NCLOUD_OBJECT_STORAGE_URL', 'https://kr.object.ncloudstorage.com/confirms-object');
+define('NCLOUD_S3_KEY', 'ncp_iam_BPAMKR3l50hXJiQ6qpSP');
+define('NCLOUD_S3_SECRET', 'ncp_iam_BPKMKRW2GU59UE59I1QftVGst6NJgnmbSc');
+define('NCLOUD_S3_BUCKET', 'confirms-object');
+define('NCLOUD_S3_ENDPOINT', 'https://kr.object.ncloudstorage.com');
\ No newline at end of file
diff --git a/app/Config/Routes.php b/app/Config/Routes.php
index 6ba30c2..f969561 100644
--- a/app/Config/Routes.php
+++ b/app/Config/Routes.php
@@ -43,6 +43,44 @@ $routes->group('board', ['namespace' => 'App\Controllers\Board'], function ($rou
});
+/**
+ * 아파트단지 DB구축 그룹
+ */
+$routes->group('article', ['namespace' => 'App\Controllers\Article'], function ($routes) {
+ // 아파트단지DB구축현황
+ $routes->get('apt/lists', 'Apt::lists');
+ $routes->get('apt/detail/(:num)', 'Apt::detail/$1');
+
+ // 관할포인트 인쇄
+ $routes->get('apt/print', 'Apt::print');
+
+
+ /** API - 아파트단지 */
+ $routes->get('apt/getAptLists', 'Apt::getAptLists');
+ $routes->post('apt/saveAptMemo', 'Apt::saveAptMemo');
+ $routes->post('apt/chgAptDamdang', 'Apt::chgAptDamdang');
+ $routes->post('apt/chgAptVideoTarget', 'Apt::chgAptVideoTarget');
+ $routes->post('apt/chkTakeAptPhotoCnt', 'Apt::chkTakeAptPhotoCnt');
+ $routes->get('apt/excel', 'Apt::excel');
+
+ /** API - 아파트단지 상세 */
+ $routes->post('apt/saveKeeper', 'Apt::saveKeeper');
+ $routes->post('apt/saveCoordinate', 'Apt::saveCoordinate');
+ $routes->post('apt/saveNote', 'Apt::saveNote');
+ $routes->post('apt/saveVideoTarget', 'Apt::saveVideoTarget');
+ $routes->post('apt/saveVideoReason', 'Apt::saveVideoReason');
+ $routes->get('apt/cateJson', 'Apt::cateJson');
+ $routes->post('apt/savePhoReason', 'Apt::savePhoReason');
+ $routes->post('apt/saveCate', 'Apt::saveCate');
+ $routes->post('apt/savePhotoView', 'Apt::savePhotoView');
+ $routes->post('apt/savePhotoView', 'Apt::savePhotoView');
+ $routes->post('apt/confirmAptInfo', 'Apt::confirmAptInfo');
+ $routes->post('apt/resendAptInfo', 'Apt::resendAptInfo');
+
+ $routes->post('apt/uploadFile', 'Apt::uploadFile');
+
+});
+
/**
* 실적관리 (results) 그룹
*/
diff --git a/app/Controllers/Article/Apt.php b/app/Controllers/Article/Apt.php
new file mode 100644
index 0000000..e431459
--- /dev/null
+++ b/app/Controllers/Article/Apt.php
@@ -0,0 +1,995 @@
+aptModel = new AptModel();
+ $this->codeModel = new CodeModel();
+ }
+
+ // 아파트단지
+ public function lists(): string
+ {
+ $codes = $this->codeModel->getCodeLists(['VIDEO_TARGET', 'APT_STEP', 'PHO_YN', 'VDO_YN']); // 코드조회
+ $sido = $this->aptModel->getAreaList(); // 지역조회
+ $bonbu = $this->aptModel->getBonbuList(); // 본부
+ $team = $this->aptModel->getTeamList(); // 팀
+ $user = $this->aptModel->getUserList(); // 유저
+
+ return view("pages/article/lists", [
+ 'codes' => $codes,
+ 'sido' => $sido,
+ 'bonbu' => $bonbu,
+ 'team' => $team,
+ 'user' => $user,
+ ]);
+ }
+
+ // 아파트단지목록 조회
+ public function getAptLists()
+ {
+
+ $start = (int) $this->request->getGet('start') ?: 0;
+ $end = (int) $this->request->getGet('length') ?: 10;
+
+ $data = [
+ 'hscp_no' => $this->request->getGet('hscp_no'), // 단지코드
+ 'pho_no' => $this->request->getGet('pho_no'), // 사진코드
+ 'srcSido' => $this->request->getGet('srcSido'), // 시|도
+ 'srcGugun' => $this->request->getGet('srcGugun'), // 시|군|구
+ 'srcDong' => $this->request->getGet('srcDong'), // 읍|면|동
+
+ 'rcpt_hscp_nm' => $this->request->getGet('rcpt_hscp_nm'), // 단지명
+ 'sdate' => $this->request->getGet('sdate'), // 시작일
+ 'edate' => $this->request->getGet('edate'), // 종료일
+ 'households_cnt1' => $this->request->getGet('households_cnt1'), // 총세대수1
+ 'households_cnt2' => $this->request->getGet('households_cnt2'), // 총세대수2
+ 'dong_cnt1' => $this->request->getGet('dong_cnt1'), // 총동수1
+ 'dong_cnt2' => $this->request->getGet('dong_cnt2'), // 총동수2
+
+ 'bonbu' => $this->request->getGet('bonbu'), // 본부
+ 'team' => $this->request->getGet('team'), // 팀
+ 'damdang' => $this->request->getGet('damdang'), // 담당
+
+ 'stat' => $this->request->getGet('stat'), // 진행상태
+ ];
+
+ $totalCount = $this->aptModel->getTotalCount($data);
+ $datas = $this->aptModel->getAptLists($start, $end, $data);
+
+ $deptStatistics = $this->aptModel->getDeptStatistics($data); // 조직별통계
+ $areaStatistics = $this->aptModel->getStatistics($data); // 지역별통계
+
+ return $this->response->setJSON(body: [
+ 'draw' => (int) ($this->request->getGetPost('draw') ?? 0), // 서버사이드면 권장
+ 'recordsTotal' => $totalCount,
+ 'recordsFiltered' => $totalCount,
+ 'data' => $datas,
+ 'widgets' => [
+ 'deptList' => $deptStatistics,
+ 'areaStats' => $areaStatistics,
+ ],
+ ]);
+
+ }
+
+ // 전체촬영불가 단지 확인
+ public function chkTakeAptPhotoCnt()
+ {
+ $data = $this->aptModel->chkTakeAptPhotoCnt();
+
+ return $this->response->setJSON(
+ body:
+ $data
+ );
+ }
+
+ // 관할포인트 인쇄 - 화면
+ public function print(): string
+ {
+ $deptSq = $this->request->getGet('depChk');
+ $dept_cnt = count($deptSq);
+
+
+ $listDept = $this->aptModel->getDeptMapList($deptSq);
+
+ if (!empty($listDept)) {
+ $lati = 0;
+ $long = 0;
+ foreach ($listDept as $dept) {
+ $lati += $dept['rcpt_y'];
+ $long += $dept['rcpt_x'];
+ }
+
+ $lati = $lati / $dept_cnt;
+ $long = $long / $dept_cnt;
+ }
+
+ return view("pages/article/printMap", [
+ // 'lati' => $lati,
+ // 'long' => $long,
+ 'listDept' => $listDept,
+ ]);
+ }
+
+ // 아파트단지 정보저장
+ public function saveAptMemo()
+ {
+ try {
+
+ $rcpt_no = $this->request->getPost('rcpt_no');
+ $video_target = $this->request->getPost('video_target');
+
+ if (empty($rcpt_no)) {
+ throw new Exception("단지정보 누락");
+ }
+
+ $params = [
+ 'target' => $video_target,
+ 'memo' => $this->request->getPost('memo') ?: '',
+ 'rcpt_no' => $rcpt_no,
+ ];
+
+ // UPDATE apt_result
+ $this->aptModel->saveAptMemo($params);
+
+
+ return $this->response->setJSON([
+ 'code' => '0',
+ 'msg' => 'success'
+ ]);
+
+
+ } catch (\Exception $e) {
+ return $this->response->setJSON([
+ 'code' => '9',
+ 'msg' => $e->getMessage(),
+ ]);
+ }
+ }
+
+ // 담당자정보변경
+ public function chgAptDamdang()
+ {
+ try {
+
+ $team = $this->request->getPost('team');
+ $damdang = $this->request->getPost(index: 'damdang');
+
+ if (empty($team)) {
+ throw new Exception("팀정보 누락");
+ }
+
+ if (empty($damdang)) {
+ throw new Exception("담당자정보 누락");
+ }
+
+
+ $rows = $this->request->getPost('rows');
+
+ $rows = json_decode($rows, true);
+
+ if (count($rows) > 0) {
+ foreach ($rows as $row) {
+ $params = [
+ $team,
+ $damdang,
+ $row['rcpt_no'],
+ ];
+
+
+ // UPDATE apt_result
+ $this->aptModel->updateAptDamdang($params);
+
+ }
+ } else {
+ throw new Exception("저장할 데이터 누락");
+ }
+
+ return $this->response->setJSON([
+ 'code' => '0',
+ 'msg' => 'success'
+ ]);
+
+
+ } catch (\Exception $e) {
+ return $this->response->setJSON([
+ 'code' => '9',
+ 'msg' => $e->getMessage(),
+ ]);
+ }
+ }
+
+
+ // 영상담당 정보 저장
+ public function chgAptVideoTarget()
+ {
+ try {
+
+ $target = $this->request->getPost('video');
+
+ if (empty($target)) {
+ throw new Exception("영상대상 정보 누락");
+ }
+
+
+ $rows = $this->request->getPost('rows');
+
+ $rows = json_decode($rows, true);
+
+ if (count($rows) > 0) {
+ foreach ($rows as $row) {
+ $params = [
+ 'target' => $target,
+ 'apt_step' => $row['apt_step'],
+ 'rcpt_no' => $row['rcpt_no'],
+ 'usr_id' => session('usr_id'),
+ ];
+
+
+ // UPDATE apt_result
+ $this->aptModel->updateAptVideoTarget($params);
+
+ }
+ } else {
+ throw new Exception("저장할 데이터 누락");
+ }
+
+ return $this->response->setJSON([
+ 'code' => '0',
+ 'msg' => 'success'
+ ]);
+
+
+ } catch (\Exception $e) {
+ return $this->response->setJSON([
+ 'code' => '9',
+ 'msg' => $e->getMessage(),
+ ]);
+ }
+ }
+
+
+ // 엑셀 다운로드
+ public function excel()
+ {
+
+
+ try {
+
+ $data = [
+ 'hscp_no' => $this->request->getGet('hscp_no'), // 단지코드
+ 'pho_no' => $this->request->getGet('pho_no'), // 사진코드
+ 'srcSido' => $this->request->getGet('srcSido'), // 시|도
+ 'srcGugun' => $this->request->getGet('srcGugun'), // 시|군|구
+ 'srcDong' => $this->request->getGet('srcDong'), // 읍|면|동
+
+ 'rcpt_hscp_nm' => $this->request->getGet('rcpt_hscp_nm'), // 단지명
+ 'sdate' => $this->request->getGet('sdate'), // 시작일
+ 'edate' => $this->request->getGet('edate'), // 종료일
+ 'households_cnt1' => $this->request->getGet('households_cnt1'), // 총세대수1
+ 'households_cnt2' => $this->request->getGet('households_cnt2'), // 총세대수2
+ 'dong_cnt1' => $this->request->getGet('dong_cnt1'), // 총동수1
+ 'dong_cnt2' => $this->request->getGet('dong_cnt2'), // 총동수2
+
+ 'bonbu' => $this->request->getGet('bonbu'), // 본부
+ 'team' => $this->request->getGet('team'), // 팀
+ 'damdang' => $this->request->getGet('damdang'), // 담당
+
+ 'stat' => $this->request->getGet('stat'), // 진행상태
+ ];
+
+ $datas = $this->aptModel->getExcelList($data);
+
+ return $this->response->setJSON(body: [
+ 'data' => $datas,
+ ]);
+
+ } catch (\Exception $e) {
+ $e->getPrevious()->getTraceAsString();
+ }
+ }
+
+
+ // 아파트단지 상세화면
+ public function detail($id = null): string
+ {
+ $rcpt_no = (int) $id;
+
+ if ($rcpt_no == null) {
+ throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
+ }
+
+ $bonbu = $this->aptModel->getBonbuList();
+ $team = $this->aptModel->getTeamList();
+ $user = $this->aptModel->getUserList();
+ $codes = $this->codeModel->getCodeLists(['VIDEO_TARGET', 'PHO_CATE1', 'PHO_CATE2']); // 코드조회
+
+ $apt = $this->aptModel->getDetail($rcpt_no);
+
+ //이미지 리스트
+ $image = $this->aptModel->getImgList($rcpt_no);
+
+ //이미지 총 개수
+ $cntAllPho = $this->aptModel->cntAllPho($rcpt_no);
+ if (empty($cntAllPho)) {
+ $cntAllPho = 0;
+ }
+ //동영상 리스트
+ $vdo = $this->aptModel->getVideoList($rcpt_no);
+ //사진및 설명정보의 카테고리 리스트
+ $cateInfo = $this->aptModel->getCateInfoList($rcpt_no);
+
+ //정보변경이력
+ $history = $this->aptModel->getHistory($rcpt_no);
+
+ $code1 = [];
+ $code2 = [];
+ $video = [];
+ if (!empty($codes)) {
+ foreach ($codes as $code) {
+ if ($code['category'] === "PHO_CATE1") {
+ array_push($code1, $code);
+ } else if ($code['category'] === "PHO_CATE2") {
+ array_push($code2, $code);
+ } else if ($code['category'] === "VIDEO_TARGET") {
+ array_push($video, $code);
+ }
+ }
+ }
+
+ // return print_r($image);
+
+ return view("pages/article/detail", [
+ 'apt' => $apt,
+ 'bonbu' => $bonbu,
+ 'team' => $team,
+ 'user' => $user,
+ 'code1' => $code1,
+ 'code2' => $code2,
+ 'video' => $video,
+ 'image' => $image,
+ 'vdo' => $vdo,
+ 'history' => $history,
+ 'cateInfo' => $cateInfo,
+ 'cntAllPho' => $cntAllPho
+ ]);
+ }
+
+ public function cateJson()
+ {
+ $params = [
+ $this->request->getGet('pho_cate1'),
+ ];
+
+ $datas = $this->aptModel->getCateJson($params);
+
+
+ return $this->response->setJSON($datas);
+ }
+
+
+ // 담당자 정보 저장
+ public function saveKeeper()
+ {
+ try {
+
+ $params = [
+ 'rcpt_no' => $this->request->getPost('rcpt_no'),
+ 'bonbu' => $this->request->getPost('bonbu'),
+ 'team' => $this->request->getPost('team'),
+ 'user' => $this->request->getPost('user'),
+ ];
+
+ // UPDATE apt_result
+ $this->aptModel->saveKeeper($params);
+
+
+ return $this->response->setJSON([
+ 'code' => '0',
+ 'msg' => 'success'
+ ]);
+
+ } catch (\Exception $e) {
+ return $this->response->setJSON([
+ 'code' => '9',
+ 'msg' => $e->getMessage(),
+ ]);
+ }
+ }
+
+ // 지도좌표 수정
+ public function saveCoordinate()
+ {
+ try {
+
+ $params = [
+ 'rcpt_no' => $this->request->getPost('rcpt_no'),
+ 'rcpt_x' => $this->request->getPost('rcpt_x'),
+ 'rcpt_y' => $this->request->getPost('rcpt_y'),
+ ];
+
+ // UPDATE apt_receipt, apt_result
+ $this->aptModel->saveCoordinate($params);
+
+
+ return $this->response->setJSON([
+ 'code' => '0',
+ 'msg' => 'success'
+ ]);
+
+ } catch (\Exception $e) {
+ return $this->response->setJSON([
+ 'code' => '9',
+ 'msg' => $e->getMessage(),
+ ]);
+ }
+ }
+
+ // 단지 특이사항 저장
+ public function saveNote()
+ {
+ try {
+
+ $params = [
+ 'rcpt_no' => $this->request->getPost('rcpt_no'),
+ 'note' => $this->request->getPost('note'),
+ ];
+
+ // UPDATE apt_result
+ $this->aptModel->saveNote($params);
+
+
+ return $this->response->setJSON([
+ 'code' => '0',
+ 'msg' => 'success'
+ ]);
+
+ } catch (\Exception $e) {
+ return $this->response->setJSON([
+ 'code' => '9',
+ 'msg' => $e->getMessage(),
+ ]);
+ }
+ }
+
+
+ // 동영상 촬영정보 저장
+ public function saveVideoTarget()
+ {
+ try {
+
+ $params = [
+ 'rcpt_no' => $this->request->getPost('rcpt_no'),
+ 'target' => $this->request->getPost('video_target'),
+ ];
+
+ // UPDATE apt_result
+ $this->aptModel->saveVideoTarget($params);
+
+
+ return $this->response->setJSON([
+ 'code' => '0',
+ 'msg' => 'success'
+ ]);
+
+ } catch (\Exception $e) {
+ return $this->response->setJSON([
+ 'code' => '9',
+ 'msg' => $e->getMessage(),
+ ]);
+ }
+ }
+
+ // 촬영불가사유 저장
+ public function saveVideoReason()
+ {
+ try {
+
+ $params = [
+ 'rcpt_no' => $this->request->getPost('rcpt_no'),
+ 'not_vdo_reson' => $this->request->getPost('not_vdo_reson'),
+ 'vdo_up_ynx' => $this->request->getPost('vdo_up_ynx'),
+ ];
+
+ // UPDATE apt_result
+ $this->aptModel->saveVideoReason($params);
+
+
+ return $this->response->setJSON([
+ 'code' => '0',
+ 'msg' => 'success'
+ ]);
+
+ } catch (\Exception $e) {
+ return $this->response->setJSON([
+ 'code' => '9',
+ 'msg' => $e->getMessage(),
+ ]);
+ }
+ }
+
+ // 사진업로드 불가사유 저장
+ public function savePhoReason()
+ {
+ try {
+
+ $params = [
+ 'rcpt_no' => $this->request->getPost('rcpt_no'),
+ 'pho_cate2' => $this->request->getPost('pho_cate2'),
+ 'pho_up_nu' => $this->request->getPost('pho_up_nu'),
+ 'usr_id' => session('usr_id'),
+ ];
+
+ // UPDATE apt_category
+ $this->aptModel->savePhoReason($params);
+
+
+ return $this->response->setJSON([
+ 'code' => '0',
+ 'msg' => 'success'
+ ]);
+
+ } catch (\Exception $e) {
+ return $this->response->setJSON([
+ 'code' => '9',
+ 'msg' => $e->getMessage(),
+ ]);
+ }
+ }
+
+ // 사진 카테고리 수정
+ public function saveCate()
+ {
+ try {
+ $phoNo = $this->request->getPost('pho_no');
+ if (!is_array($phoNo)) {
+ $phoNo = [$phoNo];
+ }
+
+ $params = [
+ 'pho_no' => $phoNo,
+ 'pho_cate1' => $this->request->getPost('pho_cate1'),
+ 'pho_cate2' => $this->request->getPost('pho_cate2'),
+ 'rcpt_no' => $this->request->getPost('rcpt_no'),
+ 'now_cate' => $this->request->getPost('nowCate2'),
+ ];
+
+ $this->aptModel->saveCate($params);
+
+ return $this->response->setJSON([
+ 'code' => '0',
+ 'msg' => 'success'
+ ]);
+
+ } catch (\Exception $e) {
+ return $this->response->setJSON([
+ 'code' => '9',
+ 'msg' => $e->getMessage(),
+ ]);
+ }
+
+ }
+
+ // 사진 노출정보 저장
+ public function savePhotoView()
+ {
+ try {
+ $phoNo = $this->request->getPost('pho_no');
+ if (!is_array($phoNo)) {
+ $phoNo = [$phoNo];
+ }
+
+ $pho_cate2 = $this->request->getPost('pho_cate2');
+
+ $params = [
+ 'pho_no' => $phoNo,
+ 'pho_view_yn' => $this->request->getPost('pho_view_yn_' . $pho_cate2),
+ 'rcpt_no' => $this->request->getPost('rcpt_no'),
+ ];
+
+ $this->aptModel->savePhotoView($params);
+
+ return $this->response->setJSON([
+ 'code' => '0',
+ 'msg' => 'success'
+ ]);
+
+ } catch (\Exception $e) {
+ return $this->response->setJSON([
+ 'code' => '9',
+ 'msg' => $e->getMessage(),
+ ]);
+ }
+ }
+
+ // 사진정보삭제
+ public function removePhoto()
+ {
+ try {
+ $phoNo = $this->request->getPost('pho_no');
+ if (!is_array($phoNo)) {
+ $phoNo = [$phoNo];
+ }
+
+ $pho_cate2 = $this->request->getPost('pho_cate2');
+
+ $params = [
+ 'pho_no' => $phoNo,
+ 'rcpt_no' => $this->request->getPost('rcpt_no'),
+ 'cate2_cd' => $this->request->getPost('cate2_cd'),
+ ];
+
+ $this->aptModel->removePhoto($params);
+
+ return $this->response->setJSON([
+ 'code' => '0',
+ 'msg' => 'success'
+ ]);
+
+ } catch (\Exception $e) {
+ return $this->response->setJSON([
+ 'code' => '9',
+ 'msg' => $e->getMessage(),
+ ]);
+ }
+ }
+
+ // 사진설명 저장
+ public function savePhoExplain()
+ {
+ try {
+
+ $params = [
+ 'rcpt_no' => $this->request->getPost('rcpt_no'),
+ 'pho_cate2' => $this->request->getPost('pho_cate2'),
+ 'pho_explain' => session('pho_explain'),
+ 'usr_id' => session('usr_id'),
+ ];
+
+ $this->aptModel->savePhoExplain($params);
+
+
+ return $this->response->setJSON([
+ 'code' => '0',
+ 'msg' => 'success'
+ ]);
+
+ } catch (\Exception $e) {
+ return $this->response->setJSON([
+ 'code' => '9',
+ 'msg' => $e->getMessage(),
+ ]);
+ }
+ }
+
+ // 검수완료 저장
+ public function confirmAptInfo()
+ {
+ try {
+
+ $rcpt_no = $this->request->getPost('rcpt_no');
+ $hscp_no = $this->request->getPost('hscp_no');
+
+ $params = [
+ 'rcpt_no' => $rcpt_no,
+ 'hscp_no' => $hscp_no,
+ ];
+
+ $apiData = $this->aptModel->new_api_photo_send_data($rcpt_no);
+
+ $ip_addr = array(
+ '172.16.100.2',
+ '172.16.100.3',
+ '172.16.100.4',
+ '172.16.100.5'
+ );
+
+ if (
+ isset($_SERVER['HTTPS']) &&
+ ($_SERVER['HTTPS'] == 'on' || $_SERVER['HTTPS'] == 1) ||
+ isset($_SERVER['HTTP_X_FORWARDED_PROTO']) &&
+ $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https'
+ ) {
+ $protocol = 'https://';
+ } else {
+ $protocol = 'http://';
+ }
+
+ if (in_array($_SERVER['SERVER_ADDR'], $ip_addr) == false) {
+ $domain = $protocol . $_SERVER['HTTP_HOST'];
+ // $domain = $protocol . 'test-admin.confirms.co.kr';
+ } else {
+ $domain = $protocol . 'admin.confirms.co.kr';
+ }
+
+
+ foreach ($apiData as $key => $d) {
+ $isExposed = ($d['use_yn'] == 'Y' ? true : false);
+ $array[] = array('mainCategory' => $d['cate1_cd'], 'subCategory' => $d['cate2_cd'], 'comment' => $d['pho_explain']);
+ $array1[] = array(
+ 'photoNumber' => $d['pho_no'],
+ 'mainCategory' => $d['cate1_cd'],
+ 'subCategory' => $d['cate2_cd'],
+ 'lat' => $d['pho_lati'],
+ 'lng' => $d['pho_long'],
+ 'url' => $d['url'],
+ 'isExposed' => $isExposed,
+ 'order' => 1
+ );
+ }
+
+ $serializedArray = array_map('serialize', $array);
+ $uniqueArray = array_unique($serializedArray);
+ $post_data_array['categories'] = array_map('unserialize', array_values($uniqueArray));
+
+ $post_data_array['photos'] = $array1;
+
+ $send_post_data = json_encode($post_data_array, JSON_UNESCAPED_UNICODE);
+
+
+ /**API시작**/
+ $result = $this->syncRequestListNew($hscp_no, $send_post_data);
+ /**API끝**/
+
+
+ if ($result['code'] == 'success') {
+ $syncId = '';
+ $this->aptModel->saveCheck($rcpt_no, $syncId);
+
+ return $this->response->setJSON([
+ 'code' => '0',
+ 'msg' => 'success'
+ ]);
+
+ } else {
+ return $this->response->setJSON([
+ 'code' => '9',
+ 'msg' => '저장실패'
+ ]);
+ }
+
+ } catch (\Exception $e) {
+ return $this->response->setJSON([
+ 'code' => '9',
+ 'msg' => $e->getMessage(),
+ ]);
+ }
+ }
+
+ // 재전송
+ public function resendAptInfo()
+ {
+ try {
+
+ $rcpt_no = $this->request->getPost('rcpt_no');
+ $hscp_no = $this->request->getPost('hscp_no');
+
+ $params = [
+ 'rcpt_no' => $rcpt_no,
+ 'hscp_no' => $hscp_no,
+ ];
+
+ $apiData = $this->aptModel->new_api_photo_send_data($rcpt_no);
+
+ $ip_addr = array(
+ '172.16.100.2',
+ '172.16.100.3',
+ '172.16.100.4',
+ '172.16.100.5'
+ );
+
+ if (
+ isset($_SERVER['HTTPS']) &&
+ ($_SERVER['HTTPS'] == 'on' || $_SERVER['HTTPS'] == 1) ||
+ isset($_SERVER['HTTP_X_FORWARDED_PROTO']) &&
+ $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https'
+ ) {
+ $protocol = 'https://';
+ } else {
+ $protocol = 'http://';
+ }
+
+ if (in_array($_SERVER['SERVER_ADDR'], $ip_addr) == false) {
+ $domain = $protocol . $_SERVER['HTTP_HOST'];
+ // $domain = $protocol . 'test-admin.confirms.co.kr';
+ } else {
+ $domain = $protocol . 'admin.confirms.co.kr';
+ }
+
+ foreach ($apiData as $key => $d) {
+ $isExposed = ($d['use_yn'] == 'Y' ? true : false);
+ $array[] = array('mainCategory' => $d['cate1_cd'], 'subCategory' => $d['cate2_cd'], 'comment' => $d['pho_explain']);
+ $array1[] = array(
+ 'photoNumber' => $d['pho_no'],
+ 'mainCategory' => $d['cate1_cd'],
+ 'subCategory' => $d['cate2_cd'],
+ 'lat' => $d['pho_lati'],
+ 'lng' => $d['pho_long'],
+ 'url' => $d['url'],
+ 'isExposed' => $isExposed,
+ 'order' => 1
+ );
+ }
+ $serializedArray = array_map('serialize', $array);
+ $uniqueArray = array_unique($serializedArray);
+ $post_data_array['categories'] = array_map('unserialize', array_values($uniqueArray));
+
+ $post_data_array['photos'] = $array1;
+ $send_post_data = json_encode($post_data_array, JSON_UNESCAPED_UNICODE);
+
+ log_message('debug', 'APT_SEND rcpt_no:::::::' . $rcpt_no . ':::::::' . $send_post_data);
+ // $send_post_data = to_han( json_encode( $post_data_array) );
+ /**API시작**/
+ $result = $this->syncRequestListNew($hscp_no, $send_post_data);
+ /**API끝**/
+
+ if ($result['code'] == 'success') {
+ $syncId = '';
+ $this->aptModel->saveResend($rcpt_no, $syncId);
+
+ return $this->response->setJSON([
+ 'code' => '0',
+ 'msg' => 'success'
+ ]);
+
+ } else {
+ return $this->response->setJSON([
+ 'code' => '9',
+ 'msg' => '저장실패'
+ ]);
+ }
+
+
+ } catch (\Exception $e) {
+ return $this->response->setJSON([
+ 'code' => '9',
+ 'msg' => $e->getMessage(),
+ ]);
+ }
+ }
+
+ // 파일업로드 - 동영상, 사진
+ public function uploadFile()
+ {
+ $lib = new MyUpload();
+
+ try {
+ $rcpt_no = $this->request->getPost('rcpt_no');
+ $uploadType = $this->request->getPost('upload_type');
+ $files = $this->request->getFiles();
+ $imgPath = "/upload/apt_file/" . $rcpt_no . "/";
+
+ $imgPath = NCLOUD_OBJECT_STORAGE_URL . $imgPath;
+ $moviePath = $imgPath;
+ $photo360Path = $imgPath;
+
+ if (!isset($files['files'])) {
+ return $this->response->setJSON([
+ 'success' => false,
+ 'msg' => '파일 없음'
+ ]);
+ }
+
+
+ if ($uploadType === "photo") {
+
+
+ // foreach ($files['files'] as $file) {
+ // if ($file->isValid()) {
+ // $file->move(WRITEPATH . 'uploads');
+
+ // INSERT apt_photo
+
+ print_r($_FILES);
+ // }
+
+
+
+ // $imageDataBlob = file_get_contents($object_storage_url);
+ // $im = new Imagick();
+ // $im->readImageBlob($imageDataBlob);
+ // $im->thumbnailImage(105, 80, false);
+ // $thumb_im = $im->getImageBlob();
+ // $lib->upload_object_storage_imagick();
+
+ // }
+
+
+ } else if ($uploadType === "video") {
+
+ }
+
+
+
+ } catch (\Exception $e) {
+ return $this->response->setJSON([
+ 'code' => '9',
+ 'msg' => $e->getMessage(),
+ ]);
+ }
+ }
+
+
+ /**
+ * 동기화요청 API
+ */
+ public function syncRequestListNew($hscp_no, $postData)
+ {
+
+ $addr = $this->apt_model->getSyncAddr();
+ $key = $addr['api_key'];
+ $server = $addr['api_server'];
+ $secret = $addr['api_secret'];
+ $urlString = $server . "/kiso/confirms/complex/" . $hscp_no . "/photo";
+
+ $postData = str_replace("\\/", '/', $postData);
+
+
+ $headers = array();
+ $headers[] = 'x-naver-client-id: ' . $key;
+ if (!empty($secret)) {
+ $headers[] = 'x-naver-client-secret: ' . $secret;
+ }
+ $headers[] = 'Accept: application/json';
+ $headers[] = 'Content-Type: application/json';
+
+ // var_dump( $urlString );
+ // var_dump( $headers );
+ log_message('debug', ' syncRequestListNew => url =>' . $urlString . '===============postData =>' . $postData);
+
+ $curl = curl_init();
+ curl_setopt_array($curl, array(
+ CURLOPT_URL => $urlString,
+ CURLOPT_RETURNTRANSFER => true,
+ CURLOPT_ENCODING => '',
+ CURLOPT_MAXREDIRS => 10,
+ CURLOPT_TIMEOUT => 0,
+ CURLOPT_FOLLOWLOCATION => true,
+ CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
+ CURLOPT_CUSTOMREQUEST => 'POST',
+ CURLOPT_POSTFIELDS => $postData,
+ CURLOPT_HTTPHEADER => $headers,
+ ));
+
+ $response = curl_exec($curl);
+ $info = curl_getinfo($curl, CURLINFO_HTTP_CODE);
+ curl_close($curl);
+
+ $result = json_decode($response, TRUE);
+
+ log_message('debug', ' syncRequestListNew => url =>' . $urlString . '===============returnData =>' . $response . '====http_code===' . $info);
+ // if ( $info == "200" ) {
+ if (isset($result) && $result['code'] == 'success') {
+ return $result;
+ } else {
+ return array('code' => 'fail', 'message' => '전송실패', "timeStamp" => date("Y-m-dTH:i:s"));
+ }
+
+ // $jsonArray = json_decode($response, TRUE);
+
+ // if ($response === false || $info['http_code'] != 200){
+ // $return = "false";
+ // }else{
+ // $jsonArray = json_decode($response, TRUE);
+ // if ($jsonArray['code'] == "success"){
+ // // $return = $jsonArray['syncList'];
+ // $return = "true";
+
+ // }else{
+ // $return = "false";
+ // }
+ // }
+ // return $return;
+ }
+}
\ No newline at end of file
diff --git a/app/Controllers/board/Notice.php b/app/Controllers/Board/Notice.php
similarity index 100%
rename from app/Controllers/board/Notice.php
rename to app/Controllers/Board/Notice.php
diff --git a/app/Controllers/home/Home.php b/app/Controllers/Home/Home.php
similarity index 100%
rename from app/Controllers/home/Home.php
rename to app/Controllers/Home/Home.php
diff --git a/app/Controllers/listfax/ListFax.php b/app/Controllers/Listfax/ListFax.php
similarity index 100%
rename from app/Controllers/listfax/ListFax.php
rename to app/Controllers/Listfax/ListFax.php
diff --git a/app/Controllers/manage/Areas.php b/app/Controllers/Manage/Areas.php
similarity index 100%
rename from app/Controllers/manage/Areas.php
rename to app/Controllers/Manage/Areas.php
diff --git a/app/Controllers/manage/Dept.php b/app/Controllers/Manage/Dept.php
similarity index 100%
rename from app/Controllers/manage/Dept.php
rename to app/Controllers/Manage/Dept.php
diff --git a/app/Controllers/manage/LoginLog.php b/app/Controllers/Manage/LoginLog.php
similarity index 100%
rename from app/Controllers/manage/LoginLog.php
rename to app/Controllers/Manage/LoginLog.php
diff --git a/app/Controllers/manage/Menu.php b/app/Controllers/Manage/Menu.php
similarity index 100%
rename from app/Controllers/manage/Menu.php
rename to app/Controllers/Manage/Menu.php
diff --git a/app/Controllers/manage/Permit.php b/app/Controllers/Manage/Permit.php
similarity index 100%
rename from app/Controllers/manage/Permit.php
rename to app/Controllers/Manage/Permit.php
diff --git a/app/Controllers/manage/Phone.php b/app/Controllers/Manage/Phone.php
similarity index 100%
rename from app/Controllers/manage/Phone.php
rename to app/Controllers/Manage/Phone.php
diff --git a/app/Controllers/manage/Scomplex.php b/app/Controllers/Manage/Scomplex.php
similarity index 100%
rename from app/Controllers/manage/Scomplex.php
rename to app/Controllers/Manage/Scomplex.php
diff --git a/app/Controllers/manage/Sms.php b/app/Controllers/Manage/Sms.php
similarity index 100%
rename from app/Controllers/manage/Sms.php
rename to app/Controllers/Manage/Sms.php
diff --git a/app/Controllers/manage/User.php b/app/Controllers/Manage/User.php
similarity index 100%
rename from app/Controllers/manage/User.php
rename to app/Controllers/Manage/User.php
diff --git a/app/Controllers/results/Assign.php b/app/Controllers/Results/Assign.php
similarity index 100%
rename from app/Controllers/results/Assign.php
rename to app/Controllers/Results/Assign.php
diff --git a/app/Controllers/results/Dept.php b/app/Controllers/Results/Dept.php
similarity index 100%
rename from app/Controllers/results/Dept.php
rename to app/Controllers/Results/Dept.php
diff --git a/app/Controllers/results/M409.php b/app/Controllers/Results/M409.php
similarity index 100%
rename from app/Controllers/results/M409.php
rename to app/Controllers/Results/M409.php
diff --git a/app/Controllers/results/M410.php b/app/Controllers/Results/M410.php
similarity index 100%
rename from app/Controllers/results/M410.php
rename to app/Controllers/Results/M410.php
diff --git a/app/Controllers/results/M411.php b/app/Controllers/Results/M411.php
similarity index 100%
rename from app/Controllers/results/M411.php
rename to app/Controllers/Results/M411.php
diff --git a/app/Controllers/results/M412.php b/app/Controllers/Results/M412.php
similarity index 100%
rename from app/Controllers/results/M412.php
rename to app/Controllers/Results/M412.php
diff --git a/app/Controllers/results/M415.php b/app/Controllers/Results/M415.php
similarity index 100%
rename from app/Controllers/results/M415.php
rename to app/Controllers/Results/M415.php
diff --git a/app/Controllers/results/M416.php b/app/Controllers/Results/M416.php
similarity index 100%
rename from app/Controllers/results/M416.php
rename to app/Controllers/Results/M416.php
diff --git a/app/Controllers/results/M417.php b/app/Controllers/Results/M417.php
similarity index 100%
rename from app/Controllers/results/M417.php
rename to app/Controllers/Results/M417.php
diff --git a/app/Controllers/results/Person.php b/app/Controllers/Results/Person.php
similarity index 100%
rename from app/Controllers/results/Person.php
rename to app/Controllers/Results/Person.php
diff --git a/app/Controllers/results/Summary.php b/app/Controllers/Results/Summary.php
similarity index 100%
rename from app/Controllers/results/Summary.php
rename to app/Controllers/Results/Summary.php
diff --git a/app/Libraries/MyUpload.php b/app/Libraries/MyUpload.php
new file mode 100644
index 0000000..27d33ed
--- /dev/null
+++ b/app/Libraries/MyUpload.php
@@ -0,0 +1,477 @@
+initialize($config);
+ }
+
+ public function initialize(array $config): self
+ {
+ foreach ($config as $k => $v) {
+ if (property_exists($this, $k)) {
+ $this->{$k} = $v;
+ }
+ }
+ return $this;
+ }
+
+ // CI3 스타일: do_upload()
+ public function do_upload(string $field = 'userfile'): bool
+ {
+ $request = service('request');
+ $file = $request->getFile($field);
+
+ if (!$file) {
+ $this->set_error('upload_no_file_selected');
+ return false;
+ }
+
+ if (!$file->isValid()) {
+ // CI3의 $_FILES 에러코드 대응(가까운 메시지로 매핑)
+ $err = $file->getError();
+ switch ($err) {
+ case UPLOAD_ERR_INI_SIZE:
+ $this->set_error('upload_file_exceeds_limit');
+ break;
+ case UPLOAD_ERR_FORM_SIZE:
+ $this->set_error('upload_file_exceeds_form_limit');
+ break;
+ case UPLOAD_ERR_PARTIAL:
+ $this->set_error('upload_file_partial');
+ break;
+ case UPLOAD_ERR_NO_FILE:
+ $this->set_error('upload_no_file_selected');
+ break;
+ case UPLOAD_ERR_NO_TMP_DIR:
+ $this->set_error('upload_no_temp_directory');
+ break;
+ case UPLOAD_ERR_CANT_WRITE:
+ $this->set_error('upload_unable_to_write_file');
+ break;
+ case UPLOAD_ERR_EXTENSION:
+ $this->set_error('upload_stopped_by_extension');
+ break;
+ default:
+ $this->set_error('upload_no_file_selected');
+ break;
+ }
+ return false;
+ }
+
+ // upload_path가 비어있으면 CI3도 실패함
+ if (!$this->upload_path) {
+ $this->set_error('upload_no_file_selected');
+ return false;
+ }
+
+ // (너가 올린 코드처럼) 디렉토리 생성
+ $this->make_dirs($this->upload_path);
+
+ // temp file / client file name
+ $this->file_temp = $file->getTempName();
+ $this->client_name = $file->getClientName();
+
+ // file size (bytes)
+ $this->file_size = (int) $file->getSize(); // bytes
+
+ // mime / type
+ $this->file_type = strtolower((string) $file->getClientMimeType());
+ $this->file_name = $this->_prep_filename($this->client_name);
+ $this->file_ext = $this->get_extension($this->file_name);
+
+ // CI3처럼 override 처리
+ if ($this->_file_name_override !== '') {
+ $this->file_name = $this->_prep_filename($this->_file_name_override);
+
+ if (strpos($this->_file_name_override, '.') === false) {
+ $this->file_name .= $this->file_ext;
+ } else {
+ $this->file_ext = $this->get_extension($this->_file_name_override);
+ }
+ }
+
+ // allowed type 체크
+ if (!$this->is_allowed_filetype($file->getClientExtension(), $this->file_type)) {
+ $this->set_error('upload_invalid_filetype');
+ return false;
+ }
+
+ // max size 체크 (CI3는 KB 기반)
+ if (!$this->is_allowed_filesize($this->file_size)) {
+ $this->set_error('upload_invalid_filesize');
+ return false;
+ }
+
+ // 파일명 정리/길이 제한/공백 제거
+ $this->file_name = $this->clean_file_name($this->file_name);
+
+ if ($this->max_filename > 0) {
+ $this->file_name = $this->limit_filename_length($this->file_name, $this->max_filename);
+ }
+
+ if ($this->remove_spaces) {
+ $this->file_name = preg_replace("/\s+/", "_", $this->file_name);
+ }
+
+ $this->orig_name = $this->file_name;
+
+ // overwrite=false면 충돌 시 파일명 변경(CI3 유사)
+ if (!$this->overwrite) {
+ $this->file_name = $this->set_filename($this->upload_path, $this->file_name);
+ if ($this->file_name === false)
+ return false;
+ }
+
+ // (주의) CI3의 do_xss_clean 완전 동일 구현은 어려움
+ // 여기서는 위험한 확장자/패턴 정도만 1차 차단 수준으로 처리(원하면 더 강화 가능)
+ if ($this->xss_clean) {
+ if (!$this->basic_xss_guard($this->file_name, $this->file_type)) {
+ $this->set_error('upload_unable_to_write_file');
+ return false;
+ }
+ }
+
+ // ===== 너의 기존 로직 핵심: 로컬 move 대신 S3 업로드 =====
+ $destKeyLikeCi3 = $this->upload_path . $this->file_name;
+
+ if (!$this->upload_object_storage($destKeyLikeCi3, $this->file_temp, 'file')) {
+ $this->set_error('upload_destination_cloud_error');
+ return false;
+ }
+
+ // 이미지면 width/height
+ $this->set_image_properties($destKeyLikeCi3, $this->file_temp);
+
+ return true;
+ }
+
+ // CI3 스타일: data()
+ public function data(): array
+ {
+ $data = [
+ 'file_name' => $this->file_name,
+ 'file_type' => $this->file_type,
+ 'file_path' => $this->upload_path,
+ 'full_path' => rtrim($this->upload_path, '/') . '/' . $this->file_name,
+ 'raw_name' => $this->raw_name($this->file_name),
+ 'orig_name' => $this->orig_name,
+ 'client_name' => $this->client_name,
+ 'file_ext' => $this->file_ext,
+ 'file_size' => $this->file_size,
+ 'is_image' => ($this->image_width > 0 && $this->image_height > 0),
+ 'image_width' => $this->image_width,
+ 'image_height' => $this->image_height,
+ 'image_type' => $this->image_type,
+ 'image_size_str' => $this->image_size_str,
+ ];
+
+ if (!empty($this->s3_data)) {
+ $data = array_merge($data, $this->s3_data);
+ }
+ return $data;
+ }
+
+ public function display_errors(string $open = '', string $close = ''): string
+ {
+ if (!$this->errors)
+ return '';
+ return $open . implode($close . $open, $this->errors) . $close;
+ }
+
+ public function get_errors(): array
+ {
+ return $this->errors;
+ }
+
+ protected function set_error(string $msg): void
+ {
+ $this->errors[] = $msg;
+ }
+
+ // --------------------------------------------------------------------
+ // === 너의 기존 S3 메서드들 (CI4용으로 client 생성만 보강) ===
+
+ public function upload_object_storage(string $key, string $temp_file, string $type = 'file'): bool
+ {
+ // CI3 코드의 경로 치환 로직 유지 (FCPATH는 CI4에도 존재)
+ $object_storage_upload_path = str_replace(FCPATH, '/', $key);
+ $object_storage_upload_path = str_replace('/image/confirms_upload/', '/upload/', $object_storage_upload_path);
+ $object_storage_upload_path = str_replace('//', '/', $object_storage_upload_path);
+ $object_storage_upload_path = str_replace('/home/www/upload/confirms_upload/', '/upload/', $object_storage_upload_path);
+
+ $s3Client = $this->makeS3Client();
+
+ try {
+ $body = file_get_contents($temp_file);
+
+ $response = $s3Client->putObject([
+ 'Bucket' => NCLOUD_S3_BUCKET,
+ 'Key' => ltrim($object_storage_upload_path, '/'),
+ 'Body' => $body,
+ 'ACL' => 'public-read',
+ ]);
+
+ $this->s3_data = [
+ 'object_storage_upload_path' => $object_storage_upload_path,
+ 'object_storage_url' => $response['ObjectURL'] ?? null,
+ ];
+
+ } catch (\Throwable $e) {
+ // 운영에서는 echo 지양. 로그로 남기는 걸 추천
+ // log_message('error', $e->getMessage());
+ return false;
+ }
+
+ return true;
+ }
+
+ public function upload_object_storage_imagick(string $key, $blobData): bool
+ {
+ $object_storage_upload_path = str_replace(FCPATH, '/', $key);
+ $object_storage_upload_path = str_replace('/image/confirms_upload/', '/upload/', $object_storage_upload_path);
+ $object_storage_upload_path = str_replace('//', '/', $object_storage_upload_path);
+ $object_storage_upload_path = str_replace('/home/www/upload/confirms_upload/', '/upload/', $object_storage_upload_path);
+
+ $s3Client = $this->makeS3Client();
+
+ try {
+ $s3Client->putObject([
+ 'Bucket' => NCLOUD_S3_BUCKET,
+ 'Key' => ltrim($object_storage_upload_path, '/'),
+ 'Body' => $blobData,
+ 'ACL' => 'public-read',
+ ]);
+ } catch (\Throwable $e) {
+ return false;
+ }
+ return true;
+ }
+
+ public function upload_object_storage_rename(string $key, string $new_name): bool
+ {
+ $s3Client = $this->makeS3Client();
+
+ try {
+ $s3Client->copyObject([
+ 'Bucket' => NCLOUD_S3_BUCKET,
+ 'CopySource' => NCLOUD_S3_BUCKET . '/' . ltrim($key, '/'),
+ 'Key' => ltrim($new_name, '/'),
+ 'ACL' => 'public-read',
+ ]);
+
+ $s3Client->deleteObject([
+ 'Bucket' => NCLOUD_S3_BUCKET,
+ 'Key' => ltrim($key, '/'),
+ ]);
+
+ return true;
+ } catch (\Throwable $e) {
+ return false;
+ }
+ }
+
+ protected function makeS3Client(): S3Client
+ {
+ // AWS SDK v2: S3Client::factory / v3+: new S3Client
+ if (method_exists(S3Client::class, 'factory')) {
+ /** @noinspection PhpUndefinedMethodInspection */
+ return S3Client::factory([
+ 'key' => NCLOUD_S3_KEY,
+ 'secret' => NCLOUD_S3_SECRET,
+ 'endpoint' => NCLOUD_S3_ENDPOINT,
+ 'debug' => true,
+ 'ssl.certificate_authority' => false,
+ ]);
+ }
+
+ return new S3Client([
+ 'endpoint' => NCLOUD_S3_ENDPOINT,
+ 'credentials' => [
+ 'key' => NCLOUD_S3_KEY,
+ 'secret' => NCLOUD_S3_SECRET,
+ ],
+ 'region' => defined('NCLOUD_S3_REGION') ? NCLOUD_S3_REGION : 'kr-standard',
+ 'version' => 'latest',
+ // (보안주의) 필요하면 verify 설정을 추가
+ // 'http' => ['verify' => false],
+ ]);
+ }
+
+ // --------------------------------------------------------------------
+ // === CI3 Upload 유사 유틸들 ===
+
+ protected function is_allowed_filetype(string $ext, string $mime): bool
+ {
+ if ($this->allowed_types === '*' || $this->allowed_types === '')
+ return true;
+
+ $ext = strtolower(ltrim($ext, '.'));
+ $allowed = array_map('strtolower', explode('|', $this->allowed_types));
+
+ return in_array($ext, $allowed, true);
+ }
+
+ protected function is_allowed_filesize(int $bytes): bool
+ {
+ if ($this->max_size <= 0)
+ return true;
+ $kb = (int) ceil($bytes / 1024);
+ return $kb <= $this->max_size;
+ }
+
+ protected function clean_file_name(string $filename): string
+ {
+ // CI3처럼 위험문자 제거
+ $bad = ['', '<', '>', '"', "'", '&', '$', '=', ';', '?', '/', '\\', '%', '#', '{', '}', '[', ']', ':', ',', '`', '|'];
+ return str_replace($bad, '', $filename);
+ }
+
+ protected function limit_filename_length(string $filename, int $length): string
+ {
+ if (mb_strlen($filename) <= $length)
+ return $filename;
+ $ext = $this->get_extension($filename);
+ $name = $this->raw_name($filename);
+ $name = mb_substr($name, 0, max(1, $length - mb_strlen($ext)));
+ return $name . $ext;
+ }
+
+ protected function set_filename(string $path, string $filename)
+ {
+ $path = rtrim($path, '/') . '/';
+ $name = $this->raw_name($filename);
+ $ext = $this->get_extension($filename);
+
+ $i = 0;
+ $candidate = $filename;
+ while (is_file($path . $candidate)) {
+ $i++;
+ $candidate = $name . '_' . $i . $ext;
+ if ($i > 9999) {
+ $this->set_error('upload_filename_overflow');
+ return false;
+ }
+ }
+ return $candidate;
+ }
+
+ protected function set_image_properties(string $keyLike, string $tempFile): void
+ {
+ // 실제 파일은 S3로 올라갔으니 임시파일에서만 추출
+ $info = @getimagesize($tempFile);
+ if (!$info)
+ return;
+
+ $this->image_width = (int) $info[0];
+ $this->image_height = (int) $info[1];
+ $this->image_type = (string) ($info['mime'] ?? '');
+ $this->image_size_str = (int) ($info[3] ?? 0);
+ }
+
+ protected function get_extension(string $filename): string
+ {
+ $x = strrchr($filename, '.');
+ return $x === false ? '' : strtolower($x);
+ }
+
+ protected function raw_name(string $filename): string
+ {
+ $ext = $this->get_extension($filename);
+ return $ext ? substr($filename, 0, -strlen($ext)) : $filename;
+ }
+
+ protected function _prep_filename(string $filename): string
+ {
+ // 다중 점 처리 등 CI3 유사
+ return str_replace(' ', '_', $filename);
+ }
+
+ protected function basic_xss_guard(string $filename, string $mime): bool
+ {
+ // CI3 do_xss_clean 완전판은 아니고 최소 방어
+ $lower = strtolower($filename);
+ if (preg_match('/\.(php|phtml|phar|php\d|cgi|pl|asp|aspx|jsp)$/i', $lower)) {
+ return false;
+ }
+ // mime이 text/html 같은 경우도 거부하고 싶으면 여기서 추가
+ return true;
+ }
+
+ // --------------------------------------------------------------------
+ // 너가 올린 make_dirs 로직 이식( VIEW_PATH 없는 부분 보완 )
+ protected function make_dirs(string $path, bool $last_is_file = false)
+ {
+ $dir_arr = explode("/", $path);
+
+ if (empty($dir_arr[0])) {
+ $path = "";
+ } elseif ($dir_arr[0] === "." || $dir_arr[0] === "..") {
+ $path = realpath($dir_arr[0]) ?: '';
+ $dir_arr[0] = "";
+ } else {
+ $path = "/";
+ }
+
+ foreach ($dir_arr as $dir) {
+ $dir = trim($dir);
+ if (!$dir)
+ continue;
+
+ if ($last_is_file) {
+ if ($dir === $dir_arr[count($dir_arr) - 1]) {
+ return false;
+ }
+ }
+
+ $path = isset($path) ? ($path . "/" . $dir) : $dir;
+
+ if (@is_dir($path))
+ continue;
+
+ @mkdir($path, 0707, true);
+ @chmod($path, 0707);
+
+ // CI3는 VIEW_PATH/index.html 복사였는데 CI4에서는 없을 수 있으니 직접 생성
+ $index = rtrim($path, '/') . '/index.html';
+ if (!is_file($index)) {
+ @file_put_contents($index, "
403 ForbiddenDirectory access is forbidden.
");
+ @chmod($index, 0606);
+ }
+ }
+ return true;
+ }
+}
diff --git a/app/Models/article/AptModel.php b/app/Models/article/AptModel.php
new file mode 100644
index 0000000..47752ca
--- /dev/null
+++ b/app/Models/article/AptModel.php
@@ -0,0 +1,1249 @@
+db->query($sql, [$gugun]);
+
+ } else if (!empty($sido)) {
+ $chk_sido = substr($sido, '0', '2');
+
+ if ($chk_sido === '36') {
+ $sido = substr($sido, '0', '4');
+ $sql = "SELECT a.region_cd, TRIM(REPLACE(a.region_nm, b.region_nm, '')) region_nm " .
+ "FROM region_codes a " .
+ "LEFT JOIN region_codes b ON b.region_cd = CONCAT(SUBSTR(a.region_cd,1,4),'000000') " .
+ "WHERE a.region_cd LIKE concat(?, '%') " .
+ "AND a.region_cd NOT LIKE '%000000' " .
+ "AND a.region_cd LIKE '%00' " .
+ "AND a.use_yn = 'Y' " .
+ "AND EXISTS (SELECT 'x' FROM region_codes c WHERE c.region_cd LIKE CONCAT(SUBSTR(a.region_cd,1,5),'%') AND c.region_cd > CONCAT(SUBSTR(a.region_cd,1,5),'00000')) " .
+ "ORDER BY a.region_nm ASC";
+ } else {
+ $sido = substr($sido, '0', '2');
+ $sql = "SELECT a.region_cd, TRIM(REPLACE(a.region_nm, b.region_nm, '')) region_nm" .
+ " FROM region_codes a" .
+ " LEFT JOIN region_codes b ON b.region_cd = CONCAT(SUBSTR(a.region_cd,1,2),'00000000')" .
+ " WHERE a.region_cd LIKE concat(?, '%')" .
+ " AND a.region_cd NOT LIKE '%00000000'" .
+ " AND a.region_cd LIKE '%00000'" .
+ " AND a.use_yn = 'Y'" .
+ " AND EXISTS (SELECT 'x' FROM region_codes c WHERE c.region_cd LIKE CONCAT(SUBSTR(a.region_cd,1,5),'%') AND c.region_cd > CONCAT(SUBSTR(a.region_cd,1,5),'00000'))" .
+ " ORDER BY a.region_nm ASC";
+ }
+
+ $query = $this->db->query($sql, [$sido]);
+ } else {
+ $sql = "SELECT a.region_cd, a.region_nm " .
+ "FROM region_codes a " .
+ "WHERE (a.region_cd LIKE '%00000000' " .
+ "AND a.use_yn = 'Y') " .
+ "OR region_cd = 3611000000;";
+
+ $query = $this->db->query($sql);
+ }
+
+
+ return $query->getResultArray();
+ }
+
+
+ // 소속본부조회
+ public function getBonbuList()
+ {
+ $sql = "SELECT dept_sq, pdept_sq, dept_nm, dept_desc, dept_head, use_yn, depth, insert_tm, insert_usr, update_tm, update_usr, lft, rgt" .
+ " FROM departments" .
+ " WHERE depth = 1" .
+ " AND use_yn = 'Y'" .
+ " ORDER BY lft";
+
+ $query = $this->db->query($sql);
+
+
+ return $query->getResultArray();
+ }
+
+ // 소속팀 조회
+ public function getTeamList()
+ {
+ $sql = "SELECT dept_sq, pdept_sq, dept_nm" .
+ " FROM departments" .
+ " WHERE depth = 2" .
+ " AND use_yn = 'Y'" .
+ " ORDER BY dept_nm";
+
+ $query = $this->db->query($sql);
+
+
+ return $query->getResultArray();
+ }
+
+ // 유저 조회
+ public function getUserList()
+ {
+ $sql = "SELECT
+ a.usr_sq, a.usr_id, a.usr_nm, a.dept_sq
+ FROM users a
+ WHERE
+ a.usr_level IN ('3','4','40','5','50','6','60','61','62','7','8','70')
+ AND a.use_yn = 'Y'
+ AND EXISTS (
+ SELECT 'x' FROM departments a1 INNER JOIN departments a2 ON a2.lft BETWEEN a1.lft AND a1.rgt AND a2.use_yn = 'Y'
+ WHERE 1=1 AND a2.dept_sq = a.dept_sq AND a1.use_yn = 'Y'
+ )
+ ORDER BY a.usr_level DESC, a.usr_nm ASC ";
+
+ $query = $this->db->query($sql);
+
+ return $query->getResultArray();
+ }
+
+ // 조직별 통계
+ public function getDeptStatistics($data)
+ {
+ $sql = "SELECT
+ b.dept_sq
+ , IFNULL(f.dept_nm, '') AS bonbu_nm
+ , IFNULL(i.dept_nm, '미지정') AS team_nm
+ , COUNT(b.dept_sq) AS cnt
+ FROM
+ apt_receipt a
+ JOIN
+ apt_result b ON b.rcpt_no = a.rcpt_no
+ LEFT JOIN
+ users c ON b.charger = c.usr_id
+ LEFT JOIN
+ region_codes e ON a.region_cd = e.region_cd
+ LEFT JOIN
+ departments i ON b.dept_sq = i.dept_sq
+ LEFT JOIN
+ departments f ON i.dept_sq = f.dept_sq
+ WHERE 1=1 ";
+
+ // 사진코드
+ if (!empty($data['pho_no'])) {
+ $sql .= "AND a.rcpt_no IN ( SELECT rcpt_no FROM apt_photo WHERE pho_no = {$data['pho_no']} AND use_yn = 'Y' AND pho_cate1 != 'V') ";
+
+ // 단지코드
+ } else if (!empty(($data['hscp_no']))) {
+ $sql .= "AND a.hasp_no LIKE CONCAT('%', '{$data['hscp_no']}', '%') ";
+ } else if (!empty($data['rcpt_no'])) {
+ $sql .= "AND a.rcpt_no = {$data['rcpt_no']}";
+ } else {
+
+ // 단지명
+ if (!empty($data['rcpt_hscp_nm'])) {
+ $sql .= "AND a.rcpt_hscp_nm LIKE CONCAT('%', '{$data['rcpt_hscp_nm']}', '%') ";
+ }
+
+ // 총세대수
+ if (!empty($data['households_cnt1'])) {
+ $sql .= "AND a.households_cnt >= {$data['households_cnt']} ";
+ }
+
+ if (!empty($data['households_cnt2'])) {
+ $sql .= "AND a.households_cnt <= {$data['households_cnt2']} ";
+ }
+
+
+ // 법정동코드로 지역구분
+ if (!empty($data['srcDong'])) {
+ $sql .= "AND a.region_cd = '{$data['srcDong']}' ";
+ } else {
+ if (!empty($data['srcGugun'])) {
+ $str_gugun = substr($data['srcGugun'], '0', '2');
+ if ($str_gugun == '36') { //세종시는 군구가 없고 바로 동이라서 예외
+ $sql .= "AND a.region_cd = '{$data['srcGugun']}' ";
+ } else {
+ $gugunPrefix = substr($data['srcGugun'], '0', '5');
+ $sql .= "AND a.region_cd LIKE '{$gugunPrefix}%' ";
+ }
+ } else {
+ if (!empty($data['srcSido'])) {
+ $sidoPrefix = substr($data['srcSido'], '0', '2');
+ $sql .= "AND a.region_cd LIKE '{$sidoPrefix}%' ";
+ }
+ }
+ }
+
+
+ //촬영일자 == 단지정보작성완료 일자
+ if (!empty($data['sdate'])) {
+ $sql .= "AND b.write_complete_tm >= '{$data['sdate']} 00:00:00' ";
+ }
+
+ if (!empty($data['edate'])) {
+ $sql .= "AND b.write_complete_tm <= '{$data['edate']} 00:00:00' ";
+ }
+
+ // 총동수
+ if (!empty($data['dong_cnt1'])) {
+ $sql .= "AND a.dong_cnt >= {$data['dong_cnt1']} ";
+ }
+
+ if (!empty($data['dong_cnt2'])) {
+ $sql .= "AND a.dong_cnt <= {$data['dong_cnt2']} ";
+ }
+
+ // 진행상태
+ if (!empty($data['stat']) && is_array($data['stat'])) {
+ $statList = "'" . implode("','", $data['stat']) . "'";
+ $sql .= " AND b.apt_step IN ({$statList}) ";
+ }
+ }
+
+ $sql .= "GROUP BY b.dept_sq
+ ORDER BY bonbu_nm ASC, team_nm ASC ";
+
+
+ $query = $this->db->query($sql);
+
+ return $query->getResultArray();
+ }
+
+ // 지역별 통계
+ public function getStatistics($data)
+ {
+ $sql = "SELECT
+ a.addr AS addr , COUNT(a.addr) AS cnt
+ FROM apt_receipt AS a
+ JOIN apt_result b ON a.rcpt_no = b.rcpt_no
+ LEFT JOIN users c ON b.charger = c.usr_id
+ LEFT JOIN region_codes e ON a.region_cd = e.region_cd
+ LEFT JOIN departments i ON b.dept_sq = i.dept_sq
+ WHERE 1=1 ";
+
+ // 사진코드
+ if (!empty($data['pho_no'])) {
+ $sql .= "AND a.rcpt_no IN ( SELECT rcpt_no FROM apt_photo WHERE pho_no = {$data['pho_no']} AND use_yn = 'Y' AND pho_cate1 != 'V') ";
+
+ // 단지코드
+ } else if (!empty(($data['hscp_no']))) {
+ $sql .= "AND a.hasp_no LIKE CONCAT('%', '{$data['hscp_no']}', '%') ";
+ } else if (!empty($data['rcpt_no'])) {
+ $sql .= "AND a.rcpt_no = {$data['rcpt_no']}";
+ } else {
+
+ // 단지명
+ if (!empty($data['rcpt_hscp_nm'])) {
+ $sql .= "AND a.rcpt_hscp_nm LIKE CONCAT('%', '{$data['rcpt_hscp_nm']}', '%') ";
+ }
+
+ // 총세대수
+ if (!empty($data['households_cnt1'])) {
+ $sql .= "AND a.households_cnt >= {$data['households_cnt']} ";
+ }
+
+ if (!empty($data['households_cnt2'])) {
+ $sql .= "AND a.households_cnt <= {$data['households_cnt2']} ";
+ }
+
+
+ // 법정동코드로 지역구분
+ if (!empty($data['srcDong'])) {
+ $sql .= "AND a.region_cd = '{$data['srcDong']}' ";
+ } else {
+ if (!empty($data['srcGugun'])) {
+ $str_gugun = substr($data['srcGugun'], '0', '2');
+ if ($str_gugun == '36') { //세종시는 군구가 없고 바로 동이라서 예외
+ $sql .= "AND a.region_cd = '{$data['srcGugun']}' ";
+ } else {
+ $gugunPrefix = substr($data['srcGugun'], '0', '5');
+ $sql .= "AND a.region_cd LIKE '{$gugunPrefix}%' ";
+ }
+ } else {
+ if (!empty($data['srcSido'])) {
+ $sidoPrefix = substr($data['srcSido'], '0', '2');
+ $sql .= "AND a.region_cd LIKE '{$sidoPrefix}%' ";
+ }
+ }
+ }
+
+
+ //촬영일자 == 단지정보작성완료 일자
+ if (!empty($data['sdate'])) {
+ $sql .= "AND b.write_complete_tm >= '{$data['sdate']} 00:00:00' ";
+ }
+
+ if (!empty($data['edate'])) {
+ $sql .= "AND b.write_complete_tm <= '{$data['edate']} 00:00:00' ";
+ }
+
+ // 총동수
+ if (!empty($data['dong_cnt1'])) {
+ $sql .= "AND a.dong_cnt >= {$data['dong_cnt1']} ";
+ }
+
+ if (!empty($data['dong_cnt2'])) {
+ $sql .= "AND a.dong_cnt <= {$data['dong_cnt2']} ";
+ }
+
+ // 진행상태
+ if (!empty($data['stat']) && is_array($data['stat'])) {
+ $statList = "'" . implode("','", $data['stat']) . "'";
+ $sql .= " AND b.apt_step IN ({$statList}) ";
+ }
+ }
+
+ $sql .= "GROUP BY a.addr ORDER BY a.addr ASC ";
+
+ $query = $this->db->query($sql);
+
+ return $query->getResultArray();
+ }
+
+ public function getTotalCount($data)
+ {
+ $sql = "SELECT
+ COUNT(*) AS cnt
+ FROM apt_receipt a
+ JOIN apt_result b ON b.rcpt_no = a.rcpt_no
+ LEFT JOIN users c ON b.charger = c.usr_id
+ LEFT JOIN region_codes e ON a.region_cd = e.region_cd
+ LEFT JOIN departments i ON b.dept_sq = i.dept_sq
+ LEFT JOIN codes f ON b.write_complete_yn = f.cd AND f.category = 'PHO_YN'
+ LEFT JOIN codes h ON b.vdo_up_ynx = h.cd AND h.category = 'VDO_YN'
+ LEFT JOIN codes g ON b.apt_step = g.cd AND g.category = 'APT_STEP'
+ LEFT JOIN apt_history j ON a.rcpt_no = j.rcpt_no AND j.changed_detail = 'C2' AND NOT EXISTS (SELECT 'x' FROM apt_history WHERE changed_detail LIKE 'A%' AND rcpt_no = j.rcpt_no) ";
+
+ $sql .= "WHERE 1=1 ";
+
+ // 사진코드
+ if (!empty($data['pho_no'])) {
+ $sql .= "AND a.rcpt_no IN ( SELECT rcpt_no FROM apt_photo WHERE pho_no = {$data['pho_no']} AND use_yn = 'Y' AND pho_cate1 != 'V') ";
+
+ // 단지코드
+ } else if (!empty(($data['hscp_no']))) {
+ $sql .= "AND a.hasp_no LIKE CONCAT('%', '{$data['hscp_no']}', '%') ";
+ } else if (!empty($data['rcpt_no'])) {
+ $sql .= "AND a.rcpt_no = {$data['rcpt_no']}";
+ } else {
+
+ // 단지명
+ if (!empty($data['rcpt_hscp_nm'])) {
+ $sql .= "AND a.rcpt_hscp_nm LIKE CONCAT('%', '{$data['rcpt_hscp_nm']}', '%') ";
+ }
+
+ // 총세대수
+ if (!empty($data['households_cnt1'])) {
+ $sql .= "AND a.households_cnt >= {$data['households_cnt']} ";
+ }
+
+ if (!empty($data['households_cnt2'])) {
+ $sql .= "AND a.households_cnt <= {$data['households_cnt2']} ";
+ }
+
+
+ // 법정동코드로 지역구분
+ if (!empty($data['srcDong'])) {
+ $sql .= "AND a.region_cd = '{$data['srcDong']}' ";
+ } else {
+ if (!empty($data['srcGugun'])) {
+ $str_gugun = substr($data['srcGugun'], '0', '2');
+ if ($str_gugun == '36') { //세종시는 군구가 없고 바로 동이라서 예외
+ $sql .= "AND a.region_cd = '{$data['srcGugun']}' ";
+ } else {
+ $gugunPrefix = substr($data['srcGugun'], '0', '5');
+ $sql .= "AND a.region_cd LIKE '{$gugunPrefix}%' ";
+ }
+ } else {
+ if (!empty($data['srcSido'])) {
+ $sidoPrefix = substr($data['srcSido'], '0', '2');
+ $sql .= "AND a.region_cd LIKE '{$sidoPrefix}%' ";
+ }
+ }
+ }
+
+
+ //촬영일자 == 단지정보작성완료 일자
+ if (!empty($data['sdate'])) {
+ $sql .= "AND b.write_complete_tm >= '{$data['sdate']} 00:00:00' ";
+ }
+
+ if (!empty($data['edate'])) {
+ $sql .= "AND b.write_complete_tm <= '{$data['edate']} 00:00:00' ";
+ }
+
+ // 총동수
+ if (!empty($data['dong_cnt1'])) {
+ $sql .= "AND a.dong_cnt >= {$data['dong_cnt1']} ";
+ }
+
+ if (!empty($data['dong_cnt2'])) {
+ $sql .= "AND a.dong_cnt <= {$data['dong_cnt2']} ";
+ }
+
+ // 진행상태
+ if (!empty($data['stat']) && is_array($data['stat'])) {
+ $statList = "'" . implode("','", $data['stat']) . "'";
+ $sql .= " AND b.apt_step IN ({$statList}) ";
+ }
+ }
+
+ // print ($sql);
+
+ $query = $this->db->query($sql);
+
+ return $query->getRow()->cnt;
+ }
+
+ // 아파트단지 조회
+ public function getAptLists($start, $end, $data)
+ {
+ $sql = "SELECT
+ a.rcpt_no, a.hscp_no, a.addr, a.addr2, a.rcpt_hscp_nm, a.pyeong_cnt, a.households_cnt, a.dong_cnt, a.rcpt_hscp_nm,
+ a.apt_cate_nm, a.rcpt_x, a.rcpt_y,
+ b.charger, b.memo, b.write_complete_yn, b.vdo_up_ynx, b.apt_step, b.video_target, b.all_no_pho,
+ b.dept_sq, b.vdo_up_tm, b.not_vdo_tm, b.check_tm, b.write_complete_tm,
+ c.usr_nm, i.dept_nm, j.changed_tm
+ FROM apt_receipt a
+ JOIN apt_result b ON b.rcpt_no = a.rcpt_no
+ LEFT JOIN users c ON b.charger = c.usr_id
+ LEFT JOIN region_codes e ON a.region_cd = e.region_cd
+ LEFT JOIN departments i ON b.dept_sq = i.dept_sq
+ LEFT JOIN codes f ON b.write_complete_yn = f.cd AND f.category = 'PHO_YN'
+ LEFT JOIN codes h ON b.vdo_up_ynx = h.cd AND h.category = 'VDO_YN'
+ LEFT JOIN codes g ON b.apt_step = g.cd AND g.category = 'APT_STEP'
+ LEFT JOIN apt_history j ON a.rcpt_no = j.rcpt_no AND j.changed_detail = 'C2' AND NOT EXISTS (SELECT 'x' FROM apt_history WHERE changed_detail LIKE 'A%' AND rcpt_no = j.rcpt_no) ";
+
+ $sql .= "WHERE 1=1 ";
+
+ // 사진코드
+ if (!empty($data['pho_no'])) {
+ $sql .= "AND a.rcpt_no IN ( SELECT rcpt_no FROM apt_photo WHERE pho_no = {$data['pho_no']} AND use_yn = 'Y' AND pho_cate1 != 'V') ";
+
+ // 단지코드
+ } else if (!empty(($data['hscp_no']))) {
+ $sql .= "AND a.hasp_no LIKE CONCAT('%', '{$data['hscp_no']}', '%') ";
+ } else if (!empty($data['rcpt_no'])) {
+ $sql .= "AND a.rcpt_no = {$data['rcpt_no']}";
+ } else {
+
+ // 단지명
+ if (!empty($data['rcpt_hscp_nm'])) {
+ $sql .= "AND a.rcpt_hscp_nm LIKE CONCAT('%', '{$data['rcpt_hscp_nm']}', '%') ";
+ }
+
+ // 총세대수
+ if (!empty($data['households_cnt1'])) {
+ $sql .= "AND a.households_cnt >= {$data['households_cnt']} ";
+ }
+
+ if (!empty($data['households_cnt2'])) {
+ $sql .= "AND a.households_cnt <= {$data['households_cnt2']} ";
+ }
+
+
+ // 법정동코드로 지역구분
+ if (!empty($data['srcDong'])) {
+ $sql .= "AND a.region_cd = '{$data['srcDong']}' ";
+ } else {
+ if (!empty($data['srcGugun'])) {
+ $str_gugun = substr($data['srcGugun'], '0', '2');
+ if ($str_gugun == '36') { //세종시는 군구가 없고 바로 동이라서 예외
+ $sql .= "AND a.region_cd = '{$data['srcGugun']}' ";
+ } else {
+ $gugunPrefix = substr($data['srcGugun'], '0', '5');
+ $sql .= "AND a.region_cd LIKE '{$gugunPrefix}%' ";
+ }
+ } else {
+ if (!empty($data['srcSido'])) {
+ $sidoPrefix = substr($data['srcSido'], '0', '2');
+ $sql .= "AND a.region_cd LIKE '{$sidoPrefix}%' ";
+ }
+ }
+ }
+
+
+ //촬영일자 == 단지정보작성완료 일자
+ if (!empty($data['sdate'])) {
+ $sql .= "AND b.write_complete_tm >= '{$data['sdate']} 00:00:00' ";
+ }
+
+ if (!empty($data['edate'])) {
+ $sql .= "AND b.write_complete_tm <= '{$data['edate']} 00:00:00' ";
+ }
+
+ // 총동수
+ if (!empty($data['dong_cnt1'])) {
+ $sql .= "AND a.dong_cnt >= {$data['dong_cnt1']} ";
+ }
+
+ if (!empty($data['dong_cnt2'])) {
+ $sql .= "AND a.dong_cnt <= {$data['dong_cnt2']} ";
+ }
+
+ // 진행상태
+ if (!empty($data['stat']) && is_array($data['stat'])) {
+ $statList = "'" . implode("','", $data['stat']) . "'";
+ $sql .= " AND b.apt_step IN ({$statList}) ";
+ }
+ }
+
+
+ $sql .= "LIMIT {$start}, {$end} ";
+
+ $query = $this->db->query($sql);
+
+ return $query->getResultArray();
+ }
+
+ // 전체촬영불가 단지 확인
+ public function chkTakeAptPhotoCnt()
+ {
+ $sql = "SELECT
+ COUNT(*) cnt
+ FROM apt_result
+ WHERE all_no_pho = 'x' ";
+
+ $query = $this->db->query($sql);
+
+ return $query->getRow()->cnt;
+ }
+
+
+ public function getDeptMapList($dept_sq)
+ {
+ $columns = "
+ b.hscp_no, b.rcpt_hscp_nm, b.rcpt_no, b.dept_sq, b.rcpt_x, b.rcpt_y,
+ b.charger, i.dept_nm, i.pdept_sq, i.dept_desc,
+ c.usr_nm, c.usr_id,
+ (SELECT pdept_sq FROM departments WHERE dept_sq = i.dept_sq) bonbu
+ ";
+
+ $builder = $this->db->table('apt_receipt a');
+
+ $builder->select($columns, false);
+ $builder->join('apt_result b', 'b.rcpt_no = a.rcpt_no');
+ $builder->join('users c', 'b.charger = c.usr_id', 'left');
+ $builder->join('region_codes e', 'a.region_cd = e.region_cd', 'left');
+ $builder->join('departments i', 'b.dept_sq = i.dept_sq', 'left');
+ $builder->join('codes f', "b.write_complete_yn = f.cd AND f.category = 'PHO_YN'", 'left');
+ $builder->join('codes h', "b.vdo_up_ynx = h.cd AND h.category = 'VDO_YN'", 'left');
+ $builder->join('codes g', "b.apt_step = g.cd AND g.category = 'APT_STEP'", 'left');
+
+ $builder->whereIn('b.dept_sq', $dept_sq);
+ $builder->orderBy('b.dept_sq', 'ASC');
+
+ $query = $builder->get();
+
+ return $query->getResultArray();
+
+ }
+
+
+ // 아파트단지정보저장
+ public function saveAptMemo($params)
+ {
+ $sql = "UPDATE apt_result SET ";
+ $sql .= "memo = '{$params['memo']}'";
+ if (!empty($params['target'])) {
+ $sql .= ", video_target = '{$params['target']}'";
+ }
+
+ $sql .= "WHERE rcpt_no = {$params['rcpt_no']} ";
+
+ $this->db->query($sql, $params);
+
+
+ if ($this->db->transStatus() === false) {
+ return [
+ 'success' => false,
+ 'msg' => '저장실패',
+ ];
+ }
+
+ // 성공
+ return [
+ 'success' => true,
+ ];
+ }
+
+ // 아파트 담당자 정보 변경
+ public function updateAptDamdang($params)
+ {
+ $sql = "UPDATE apt_result SET ";
+ $sql .= "dept_sq = ?, charger = ? ";
+ $sql .= "WHERE rcpt_no = ? ";
+
+ $this->db->query($sql, $params);
+
+
+ if ($this->db->transStatus() === false) {
+ return [
+ 'success' => false,
+ 'msg' => '저장실패',
+ ];
+ }
+
+ // 성공
+ return [
+ 'success' => true,
+ ];
+ }
+
+ // 영상대상 정보 저장
+ public function updateAptVideoTarget($params)
+ {
+ $sql = "UPDATE apt_result SET ";
+ $sql .= "video_target = '{$params['target']}' WHERE rcpt_no = {$params['rcpt_no']} ";
+
+ $this->db->query($sql, $params);
+
+
+ if ($this->db->transStatus() === false) {
+ return [
+ 'success' => false,
+ 'msg' => '저장실패',
+ ];
+ }
+
+ if ($params['target'] == 'Y') {
+ $changed_type = "영상 촬영 대상 지정";
+ } else {
+ $changed_type = "영상 촬영 대상 제외('" . $params['target'] . "')";
+ }
+
+ // INSERT apt_history
+ $this->saveHistory($params['rcpt_no'], $params['apt_step'], 'H', $changed_type, $params['usr_id']);
+
+ // 성공
+ return [
+ 'success' => true,
+ ];
+ }
+
+ // 엑셀 다운로드
+ public function getExcelList($data)
+ {
+ $sql = "SELECT
+ (SELECT cd_nm FROM codes WHERE category = 'APT_STEP' AND cd = b.apt_step) AS '진행상태',
+ a.hscp_no AS '단지코드',
+ a.addr AS '주소',
+ a.addr2 AS '상세주소',
+ a.rcpt_hscp_nm AS '단지구역명',
+ a.households_cnt AS '총세대수',
+ a.dong_cnt AS '총동수',
+ c.usr_nm AS '방문담당',
+ b.write_complete_yn AS '사진촬영',
+ CASE WHEN b.vdo_up_ynx = 'X' THEN '촬영불가' ELSE b.vdo_up_ynx END AS '영상촬영',
+ (SELECT cd_nm FROM codes WHERE category = 'VIDEO_TARGET' AND cd = b.video_target) AS '영상대상',
+ b.memo AS '메모',
+ b.write_complete_tm AS '사진업로드',
+ b.vdo_up_tm AS '영상업로드',
+ b.not_vdo_tm AS '촬영불가',
+ b.check_tm AS '검수',
+ b.all_no_pho AS '단지 전체 촬영불가'
+ FROM apt_receipt a
+ JOIN apt_result b ON b.rcpt_no = a.rcpt_no
+ LEFT JOIN users c ON b.charger = c.usr_id
+ LEFT JOIN region_codes e ON a.region_cd = e.region_cd
+ LEFT JOIN departments i ON b.dept_sq = i.dept_sq
+ LEFT JOIN codes f ON b.write_complete_yn = f.cd AND f.category = 'PHO_YN'
+ LEFT JOIN codes h ON b.vdo_up_ynx = h.cd AND h.category = 'VDO_YN'
+ LEFT JOIN codes g ON b.apt_step = g.cd AND g.category = 'APT_STEP'
+ LEFT JOIN apt_history j ON a.rcpt_no = j.rcpt_no AND j.changed_detail = 'C2' AND NOT EXISTS (SELECT 'x' FROM apt_history WHERE changed_detail LIKE 'A%' AND rcpt_no = j.rcpt_no) ";
+
+ $sql .= "WHERE 1=1 ";
+
+ // 사진코드
+ if (!empty($data['pho_no'])) {
+ $sql .= "AND a.rcpt_no IN ( SELECT rcpt_no FROM apt_photo WHERE pho_no = {$data['pho_no']} AND use_yn = 'Y' AND pho_cate1 != 'V') ";
+
+ // 단지코드
+ } else if (!empty(($data['hscp_no']))) {
+ $sql .= "AND a.hasp_no LIKE CONCAT('%', '{$data['hscp_no']}', '%') ";
+ } else if (!empty($data['rcpt_no'])) {
+ $sql .= "AND a.rcpt_no = {$data['rcpt_no']}";
+ } else {
+
+ // 단지명
+ if (!empty($data['rcpt_hscp_nm'])) {
+ $sql .= "AND a.rcpt_hscp_nm LIKE CONCAT('%', '{$data['rcpt_hscp_nm']}', '%') ";
+ }
+
+ // 총세대수
+ if (!empty($data['households_cnt1'])) {
+ $sql .= "AND a.households_cnt >= {$data['households_cnt']} ";
+ }
+
+ if (!empty($data['households_cnt2'])) {
+ $sql .= "AND a.households_cnt <= {$data['households_cnt2']} ";
+ }
+
+
+ // 법정동코드로 지역구분
+ if (!empty($data['srcDong'])) {
+ $sql .= "AND a.region_cd = '{$data['srcDong']}' ";
+ } else {
+ if (!empty($data['srcGugun'])) {
+ $str_gugun = substr($data['srcGugun'], '0', '2');
+ if ($str_gugun == '36') { //세종시는 군구가 없고 바로 동이라서 예외
+ $sql .= "AND a.region_cd = '{$data['srcGugun']}' ";
+ } else {
+ $gugunPrefix = substr($data['srcGugun'], '0', '5');
+ $sql .= "AND a.region_cd LIKE '{$gugunPrefix}%' ";
+ }
+ } else {
+ if (!empty($data['srcSido'])) {
+ $sidoPrefix = substr($data['srcSido'], '0', '2');
+ $sql .= "AND a.region_cd LIKE '{$sidoPrefix}%' ";
+ }
+ }
+ }
+
+
+ //촬영일자 == 단지정보작성완료 일자
+ if (!empty($data['sdate'])) {
+ $sql .= "AND b.write_complete_tm >= '{$data['sdate']} 00:00:00' ";
+ }
+
+ if (!empty($data['edate'])) {
+ $sql .= "AND b.write_complete_tm <= '{$data['edate']} 00:00:00' ";
+ }
+
+ // 총동수
+ if (!empty($data['dong_cnt1'])) {
+ $sql .= "AND a.dong_cnt >= {$data['dong_cnt1']} ";
+ }
+
+ if (!empty($data['dong_cnt2'])) {
+ $sql .= "AND a.dong_cnt <= {$data['dong_cnt2']} ";
+ }
+
+ // 진행상태
+ if (!empty($data['stat']) && is_array($data['stat'])) {
+ $statList = "'" . implode("','", $data['stat']) . "'";
+ $sql .= " AND b.apt_step IN ({$statList}) ";
+ }
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->getResultArray();
+ }
+
+ // 이력 저장
+ public function saveHistory($rcpt_no, $apt_step, $changed_type, $changed_detail, $charged_id)
+ {
+ $sql = "INSERT INTO apt_history" .
+ " (rcpt_no, apt_step, changed_type, changed_detail, charged_id, changed_tm)" .
+ " VALUES (?, ?, ?, ?, ?, NOW())";
+ $data = [
+ $rcpt_no,
+ $apt_step,
+ $changed_type,
+ $changed_detail,
+ $charged_id
+ ];
+ $res = $this->db->query($sql, $data);
+ }
+
+
+ /**
+ * 상세페이지
+ */
+ public function getDetail($rcpt_no)
+ {
+ $sql = "SELECT
+ a.rcpt_no, a.hscp_no, a.addr, a.addr2, a.rcpt_hscp_nm, a.move_ym, a.households_cnt, a.dong_cnt, a.pyeong_cnt, a.apt_cate_nm, a.region_cd, a.rcpt_x, a.rcpt_y, a.pho_exept_yn
+ ,a.rdate, DATE_FORMAT(a.rdate, '%Y-%m-%d') as rdate_dt ,DATE_FORMAT(a.rdate, '%H:%i:%s') as rdate_tm
+ ,b.vdo_up_tm, DATE_FORMAT(b.vdo_up_tm, '%Y-%m-%d') as rdate_dt_vdo ,DATE_FORMAT(b.vdo_up_tm, '%H:%i:%s') as rdate_tm_vdo
+ ,b.not_vdo_tm, DATE_FORMAT(b.not_vdo_tm, '%Y-%m-%d') as rdate_dt_nvdo ,DATE_FORMAT(b.not_vdo_tm, '%H:%i:%s') as rdate_tm_nvdo
+ ,b.check_tm, DATE_FORMAT(b.check_tm, '%Y-%m-%d') as rdate_dt_chk ,DATE_FORMAT(b.check_tm, '%H:%i:%s') as rdate_tm_chk
+ ,b.memo, b.note, b.video_target, b.vdo_up_ynx, b.not_vdo_reson, b.apt_step, b.not_vdo_tm, b.check_yn, b.resend_yn, b.write_complete_yn, b.all_no_pho
+ ,b.write_complete_tm, DATE_FORMAT(b.write_complete_tm, '%Y-%m-%d') as rdate_dt_cmpl ,DATE_FORMAT(b.write_complete_tm, '%H:%i:%s') as rdate_tm_cmpl
+ ,b.charger, b.dept_sq ,(SELECT pdept_sq FROM departments WHERE dept_sq = b.dept_sq) bonbu
+ FROM
+ apt_receipt a
+ JOIN apt_result b ON a.rcpt_no = b.rcpt_no
+
+ WHERE a.rcpt_no = ?";
+
+ $query = $this->db->query($sql, [$rcpt_no]);
+
+ return $query->getRowArray();
+ }
+
+ // 이미지리스트
+ public function getImgList($rcpt_no)
+ {
+ $sql = "SELECT pho_no,rcpt_no,pho_cate1,pho_cate2,pho_lati,pho_long,filenm,filenm_up,view_odr,pho_view_yn,pho_date,insert_tm,file_path,use_yn,thumb_path,thumb_nm,cloud_upload_yn" .
+ " FROM apt_photo" .
+ " WHERE rcpt_no = ?" .
+ " AND use_yn = 'Y'";
+
+ $query = $this->db->query($sql, [$rcpt_no]);
+
+ return $query->getResultArray();
+ }
+
+ // 이미지 총 개수
+ public function cntAllPho($rcpt_no)
+ {
+ $sql = "SELECT COUNT(*) cnt " .
+ " FROM apt_photo" .
+ " WHERE rcpt_no= ?" .
+ " AND use_yn = 'Y'" .
+ " AND pho_cate1 != 'V'";
+
+ $query = $this->db->query($sql, [$rcpt_no]);
+
+ return $query->getRow()->cnt;
+ }
+
+ // 동영상 리스트
+ public function getVideoList($rcpt_no)
+ {
+ $sql = "SELECT pho_no,rcpt_no,pho_cate1,insert_tm,use_yn,file_path,filenm_up" .
+ " FROM apt_photo" .
+ " WHERE rcpt_no = ?" .
+ " AND pho_cate1 = 'V'" .
+ " AND use_yn = 'Y'";
+
+ $query = $this->db->query($sql, [$rcpt_no]);
+
+ return $query->getRowArray();
+ }
+
+ // 사진및 설명정보의 카테고리 리스트
+ public function getCateInfoList($rcpt_no)
+ {
+ $sql = "SELECT rcpt_no, pho_cate2, pho_explain, pho_up_nu" .
+ " FROM apt_category" .
+ " WHERE rcpt_no = ?";
+
+ $query = $this->db->query($sql, [$rcpt_no]);
+
+ return $query->getResultArray();
+ }
+
+ // 정보변경이력
+ public function getHistory($rcpt_no)
+ {
+ $sql = " SELECT seq," .
+ " rcpt_no, " .
+ " apt_step, get_code_name('APT_STEP',apt_step) AS apt_step_nm, " .
+ " changed_type, get_code_name('APT_CHANGED_TYPE',changed_type) AS changed_type_nm, " .
+ " changed_detail, get_code_name('APT_CHANGED_DETAIL',changed_detail) AS changed_detail_nm, " .
+ " charged_id, " .
+ " changed_tm, DATE_FORMAT(changed_tm, '%Y-%m-%d') as rdate_dt, DATE_FORMAT(changed_tm, '%H:%i:%s') as rdate_tm" .
+ " FROM apt_history" .
+ " WHERE rcpt_no = ?" .
+ " ORDER BY changed_tm DESC";
+
+ $query = $this->db->query($sql, [$rcpt_no]);
+
+ return $query->getResultArray();
+ }
+
+ // 사진 카테고리 조회
+ public function getCateJson($params)
+ {
+ $sql = "SELECT cd, cd_nm" .
+ " FROM codes" .
+ " WHERE cd LIKE CONCAT(?, '%')" .
+ " AND category = 'PHO_CATE2'";
+
+ $query = $this->db->query($sql, [$params]);
+
+
+ return $query->getResultArray();
+ }
+
+ // 담당자정보저장
+ public function saveKeeper($params)
+ {
+ $sql = "UPDATE apt_result SET
+ dept_sq = {$params['team']}, charger = '{$params['user']}'
+ WHERE rcpt_no = {$params['rcpt_no']}
+ ";
+
+
+ if ($this->db->query($sql) === false) {
+ return [
+ 'success' => false,
+ 'msg' => '저장실패',
+ ];
+ }
+
+ // 성공
+ return [
+ 'success' => true,
+ ];
+ }
+
+ // 지도 좌표저장
+ public function saveCoordinate($params)
+ {
+ $sql = "UPDATE apt_receipt SET
+ rcpt_x = '{$params['rcpt_x']}',
+ rcpt_y = '{$params['rcpt_y']}'
+ WHERE rcpt_no = {$params['rcpt_no']}";
+
+ if ($this->db->query($sql) === false) {
+ return [
+ 'success' => false,
+ 'msg' => 'apt_receipt 저장 실패',
+ ];
+ }
+
+ $sql = "UPDATE apt_result SET
+ rcpt_x = '{$params['rcpt_x']}',
+ rcpt_y = '{$params['rcpt_y']}'
+ WHERE rcpt_no = {$params['rcpt_no']}";
+
+ if ($this->db->query($sql) === false) {
+ return [
+ 'success' => false,
+ 'msg' => 'apt_result 저장 실패',
+ ];
+ }
+
+ return [
+ 'success' => true,
+ ];
+ }
+
+ // 단지 특이사항 저장
+ public function saveNote($params)
+ {
+ $sql = "UPDATE apt_result SET
+ note = '{$params['note']}'
+ WHERE rcpt_no = {$params['rcpt_no']}";
+
+ if ($this->db->query($sql) === false) {
+ return [
+ 'success' => false,
+ 'msg' => '저장 실패',
+ ];
+ }
+
+ return [
+ 'success' => true,
+ ];
+ }
+
+ // 동영상 촬영정보 저장
+ public function saveVideoTarget($params)
+ {
+ $sql = "UPDATE apt_result SET
+ video_target = '{$params['target']}'
+ WHERE rcpt_no = {$params['rcpt_no']}
+ ";
+
+
+ if ($this->db->query($sql) === false) {
+ return [
+ 'success' => false,
+ 'msg' => '저장 실패',
+ ];
+ }
+
+ $now = $this->getDetail($params['rcpt_no']);
+ if ($params['target'] == 'Y') {
+ $changed_type = "영상 촬영 대상 지정";
+ } else {
+ $changed_type = "영상 촬영 대상 제외('" . $params['target'] . "')";
+ }
+
+ $this->saveHistory($params['rcpt_no'], $now['apt_step'], 'H', $changed_type, session('usr_id'));
+
+ return [
+ 'success' => true,
+ ];
+ }
+
+ // 촬영불가사유 저장
+ public function saveVideoReason($params)
+ {
+ $sql = "UPDATE apt_result SET
+ not_vdo_reason = '{$params['not_vdo_reason']}'
+ , vdo_up_ynx = '{$params['vdo_up_ynx']}'
+ , not_vdo_tm = NOW()
+ , apt_step = CASE WHEN write_complete_yn = 'Y' THEN 'S04' ELSE 'S03' END
+ WHERE rcpt_no = {$params['rcpt_no']}
+ ";
+
+ if ($this->db->query($sql) === false) {
+ return [
+ 'success' => false,
+ 'msg' => '저장 실패',
+ ];
+ }
+
+ $now = $this->getDetail($params['rcpt_no']);
+ $this->saveHistory($params['rcpt_no'], $now['apt_step'], 'G', 'G1', session('usr_id'));
+
+
+ return [
+ 'success' => true,
+ ];
+ }
+
+ // 사진업로드 불가사유 저장
+ public function savePhoReason($params)
+ {
+ $sql = "INSERT INTO apt_category
+ (rcpt_no, pho_cate2, pho_up_nu, insert_tm, insert_tm, insert_user_id)
+ VALUES
+ ({$params['rcpt_no']}, '{$params['pho_cate2']}', '{$params['pho_up_nu']}', NOW(), {$params['usr_id']})
+ ";
+
+ if ($this->db->query($sql) === false) {
+ return [
+ 'success' => false,
+ 'msg' => '저장 실패',
+ ];
+ }
+
+ return [
+ 'success' => true,
+ ];
+ }
+
+
+ // 사진 카테고리 수정
+ public function saveCate($params)
+ {
+
+ $data = [
+ 'pho_cate1' => $params['pho_cate1'],
+ 'pho_cate2' => $params['pho_cate2'],
+ ];
+
+ $this->db->where_in('pho_no', $params['pho_no']);
+ $result = $this->db->update('apt_photo', $data);
+
+ if ($result === false) {
+ return [
+ 'success' => false,
+ 'msg' => 'DB 업데이트 실패',
+ ];
+ }
+
+ $sql2 = " DELETE FROM apt_category" . //2.조건에맞는거 = 해당카테고리에 사진이 없을때 삭제
+ " WHERE rcpt_no = {$params['rcpt_no']}" .
+ " AND pho_cate2 = '{$params['nowCate2']}'" .
+ " AND NOT EXISTS ( SELECT 'x' FROM apt_photo b" .
+ " WHERE b.rcpt_no = apt_category.rcpt_no" .
+ " AND b.pho_cate2 = apt_category.pho_cate2" .
+ " AND b.use_yn = 'Y')";
+
+ if ($this->db->query($sql2) === false) {
+ return [
+ 'success' => false,
+ 'msg' => '처리 실패',
+ ];
+ }
+
+ $sql3 = " UPDATE apt_category" . //3. 이동할 카테고리에 이미 불가사유가 있다면 불가사유 null로 업데이트
+ " SET pho_up_nu = NULL" .
+ " WHERE rcpt_no = {$params['rcpt_no']}" .
+ " AND pho_cate2 = '{$params['pho_cate2']}'" .
+ " AND pho_up_nu IS NOT NULL";
+
+
+ if ($this->db->query($sql3) === false) {
+ return [
+ 'success' => false,
+ 'msg' => '처리 실패',
+ ];
+ }
+
+ $now = $this->getDetail($params['rcpt_no']);
+ $this->saveHistory($params['rcpt_no'], $now['apt_step'], 'D', 'D1', session('usr_id'));
+
+ return [
+ 'success' => true,
+ ];
+ }
+
+ // 사진 노출여부 수정
+ public function savePhotoView($params)
+ {
+ $data = [
+ 'pho_view_yn' => $params['pho_view_yn'],
+ ];
+
+ $builder = $this->db->table('apt_photo');
+
+ $builder->whereIn('pho_no', $params['pho_no']);
+ // $builder->where('rcpt_no', $params['rcpt_no']);
+
+ if (!$builder->update($data)) {
+ return [
+ 'success' => false,
+ 'msg' => '처리 실패',
+ ];
+ }
+
+ $now = $this->getDetail($params['rcpt_no']);
+ if ($params['pho_view_yn'] === 'Y') {//노출시작
+ $this->saveHistory($params['rcpt_no'], $now['apt_step'], 'E', 'E1', session('usr_id'));
+ } else {//종료
+ $this->saveHistory($params['rcpt_no'], $now['apt_step'], 'E', 'E2', session('usr_id'));
+ }
+
+ return [
+ 'success' => true,
+ ];
+ }
+
+ // 사진정보삭제
+ public function removePhoto($params)
+ {
+ $data = [
+ 'use_yn' => 'N',
+ ];
+
+ $builder = $this->db->table('apt_photo');
+
+ $builder->whereIn('pho_no', $params['pho_no']);
+ $builder->where('rcpt_no', $params['rcpt_no']);
+
+ if (!$builder->update()) {
+ return [
+ 'success' => false,
+ 'msg' => '처리 실패',
+ ];
+ }
+
+ $sql2 = "SELECT pho_cate2 FROM apt_photo
+ WHERE rcpt_no = {$params['rcpt_no']}
+ AND pho_cate = '{$params['cate2_cd']}'
+ AND use_yn = 'Y'
+ ";
+
+ $query2 = $this->db->query($sql2);
+
+ if ($this->db->transStatus() == false) {
+ return [
+ 'success' => false,
+ 'msg' => '처리 실패',
+ ];
+ }
+
+ if ($query2->getNumRows() > 0) {
+ $sql3 = "DELETE FROM apt_category
+ WHERE rcpt_no = {$params['rcpt_no']}
+ AND pho_cate2 = '{$params['cate2_cd']}'
+ ";
+
+ $query3 = $this->db->query($sql3);
+ }
+
+ return [
+ 'success' => true,
+ ];
+ }
+
+
+ // 사진설명 저장
+ public function savePhoExplain($params)
+ {
+ $sql = "INSERT INTO apt_category
+ (rcpt_no, pho_cate2, pho_explain, insert_tm, insert_user_id)
+ VALUES
+ ({$params['rcpt_no']}, '{$params['pho_cate2']}', '{$params['pho_explain']}', NOW(), '{$params['usr_id']}')
+ ";
+
+ if ($this->db->query($sql) === false) {
+ return [
+ 'success' => false,
+ 'msg' => '저장실패',
+ ];
+ }
+
+ $now = $this->getDetail($params['rcpt_no']);
+ $this->saveHistory($params['rcpt_no'], $now['apt_step'], 'D', '사진설명 작성', $params['usr_id']);
+
+ // 성공
+ return [
+ 'success' => true,
+ ];
+ }
+
+
+ // 단지실사 API 정보
+ public function new_api_photo_send_data($rcpt_no)
+ {
+ $sql = "SELECT c2.cd_nm as cate1_nm , c1.cd_nm cate2_nm , c1.cd_naver cate2_cd , c2.cd_naver cate1_cd , ap.pho_no , ap.pho_view_yn FROM apt_category ac
+ LEFT JOIN codes c1 ON ac.pho_cate2 = c1.cd AND c1.category = 'PHO_CATE2'
+ LEFT JOIN codes c2 ON LEFT( c1.cd_naver , 2) = LEFT ( c2.cd_naver , 2) AND c2.category = 'PHO_CATE1'
+ LEFT JOIN apt_photo ap ON c1.cd = ap.pho_cate2 AND ap.rcpt_no = ac.rcpt_no
+ WHERE ac.rcpt_no = ?
+ AND pho_no IS NOT NULL
+ group by c1.cd_naver ";
+
+ $query = $this->db->query($sql, [$rcpt_no]);
+
+ return $query->getResultArray();
+ }
+
+ // 검수완료
+ public function saveCheck($rcpt_no, $syncId)
+ {
+ $sql = "UPDATE apt_result" .
+ " SET check_yn = 'Y'" .
+ " ,check_tm = NOW()" .
+ " ,apt_step = 'S05'" .
+ " ,syncid = ?" .
+ " WHERE rcpt_no = ? ";
+
+ if ($this->db->query($sql, [$syncId, $rcpt_no]) === false) {
+ return [
+ 'success' => false,
+ 'msg' => '저장실패',
+ ];
+ }
+
+ $this->saveHistory($rcpt_no, 'S05', 'I', 'I1', session('usr_id'));
+
+ return [
+ 'success' => true,
+ ];
+ }
+
+ // 재전송
+ public function saveResend($rcpt_no, $syncId)
+ {
+ $sql = " UPDATE apt_result" .
+ " SET resend_yn = 'Y'" .
+ " ,check_tm = NOW()" .
+ " ,syncid = ?" .
+ " WHERE rcpt_no = ? ";
+
+
+ if ($this->db->query($sql, [$syncId, $rcpt_no]) === false) {
+ return [
+ 'success' => false,
+ 'msg' => '저장실패',
+ ];
+ }
+
+ $this->saveHistory($rcpt_no, 'S05', 'J', 'J1', session('usr_id'));
+
+ return [
+ 'success' => true,
+ ];
+ }
+}
\ No newline at end of file
diff --git a/app/Models/common/CodeModel.php b/app/Models/common/CodeModel.php
index b273109..9fdcdce 100644
--- a/app/Models/common/CodeModel.php
+++ b/app/Models/common/CodeModel.php
@@ -14,9 +14,44 @@ class CodeModel extends Model
" WHERE category = ?" .
" AND use_yn = 'Y'" .
" ORDER BY view_odr";
- $data = array($category);
+ $data = [$category];
$query = $this->db->query($sql, $data);
return $query->getResultArray();
}
+
+
+ public function getCodeLists($data): array
+ {
+ return $this->db->table('codes')
+ ->select('category, category_nm, cd, cd_nm')
+ ->whereIn('category', $data)
+ ->where('use_yn', 'Y')
+ ->orderBy('view_odr')
+ ->get()
+ ->getResultArray();
+ }
+
+ public function getCategoryCodeList($category = array(), $useYn = '')
+ {
+ $this->db->select('category, cd, cd_nm, use_yn');
+ $this->db->from('codes');
+ $this->db->where_in('category', $category);
+ if (!empty($useYn)) {
+ $this->db->where('use_yn', $useYn);
+ }
+ $this->db->order_by('category', 'asc');
+ $this->db->order_by('view_odr', 'asc');
+
+ $query = $this->db->get();
+
+ //echo $this->db->last_query()."
";
+
+ //여기 아래부분을 해줘야 배열을 카테고리로 뽑아쓸수있다 위에는 배열에 배열이담김
+ $codes = [];
+ foreach ($query->getResultArray() as $row) {
+ $codes[$row['category']][] = ['cd' => $row['cd'], 'cd_nm' => $row['cd_nm']];
+ }
+ return $codes;
+ }
}
\ No newline at end of file
diff --git a/app/Views/pages/article/detail.php b/app/Views/pages/article/detail.php
new file mode 100644
index 0000000..1486799
--- /dev/null
+++ b/app/Views/pages/article/detail.php
@@ -0,0 +1,1614 @@
+= $this->extend('layouts/main') ?>
+
+= $this->section('content') ?>
+
+
+아파트단지 DB구축 상세
+
+
+
+
+
+
+
단지 정보
+
+
+
+
+
+
+
+
+
+ | 단지코드 |
+ = esc($apt['hscp_no'] ?? '') ?> |
+ 주소 |
+ = esc($apt['addr'] ?? '') ?> |
+
+
+ | 단지명 |
+ = esc($apt['rcpt_hscp_nm'] ?? '') ?> |
+ 상세주소 |
+ = esc($apt['addr2'] ?? '') ?> |
+
+
+ | 입주년월 |
+ = esc($apt['move_ym'] ?? '') ?> |
+ 총세대수 |
+ = esc($apt['households_cnt'] ?? '') ?> |
+
+
+ | 총동수 |
+ = esc($apt['dong_cnt'] ?? '') ?> |
+ 메모 |
+
+
+ |
+
+
+
+
+
+
+
+
담당자 정보
+
+
+
+
+
+
+
+
+
+
+
+ | 관할본부 |
+
+
+ |
+
+ 담당팀 |
+
+
+ |
+
+ 담당자 |
+
+
+ |
+
+
+
+ |
+
+
+
+
+
+
+
+
단지 상태 정보
+
+
+
+
+
+
+
+
+
+
+ | 상태내역 |
+ 대상업로드 |
+ 사진업로드 |
+ 영상업로드 |
+ 영상촬영불가 |
+ 검수 |
+
+
+
+ = esc($apt['rdate_dt'] ?? '') ?> = esc($apt['rdate_tm'] ?? '') ?>
+ |
+
+
+
+ = esc($apt['rdate_dt_cmpl'] ?? '') ?> = esc($apt['rdate_tm_cmpl'] ?? '') ?>
+
+
+ |
+
+ = esc($apt['rdate_dt_vdo'] ?? '') ?> = esc($apt['rdate_tm_vdo'] ?? '') ?>
+ |
+
+ = esc($apt['rdate_dt_nvdo'] ?? '') ?> = esc($apt['rdate_tm_nvdo'] ?? '') ?>
+ |
+
+ = esc($apt['rdate_dt_chk'] ?? '') ?> = esc($apt['rdate_tm_chk'] ?? '') ?>
+ |
+
+
+
+
+
+
+
+
+
+
단지 영상 정보
+
+
+
+
+ | 동영상 |
+
+ 
+
+ |
+ 동영상 촬영불가 |
+
+
+ |
+ 영상촬영대상 |
+
+
+
+
+
+
+
+
+
+ |
+
+
+ | 촬영불가사유 |
+
+
+ |
+
+
+
+
+
+
+
+
+
단지 사진 기본 설정
+
+
+
+
+
+
+
+
+
+
+ | 사진 일괄업로드 |
+
+
+ |
+ 사진 일괄삭제 |
+
+
+ |
+ 단지정보 작성완료 |
+
+
+ |
+
+
+
+
+
+
+
+
단지 사진 및 설명 정보
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+= $this->section('modals') ?>
+
+
+
+= $this->endSection() ?>
+
+
+
+
+
+
+
+= $this->endSection() ?>
\ No newline at end of file
diff --git a/app/Views/pages/article/lists.php b/app/Views/pages/article/lists.php
new file mode 100644
index 0000000..9b15d45
--- /dev/null
+++ b/app/Views/pages/article/lists.php
@@ -0,0 +1,1302 @@
+= $this->extend('layouts/main') ?>
+
+= $this->section('content') ?>
+
+
+아파트단지 DB구축 현황
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+= $this->section('modals') ?>
+
+= $this->endSection() ?>
+
+
+
+
+
+= $this->endSection() ?>
\ No newline at end of file
diff --git a/app/Views/pages/article/printMap.php b/app/Views/pages/article/printMap.php
new file mode 100644
index 0000000..7ff91cc
--- /dev/null
+++ b/app/Views/pages/article/printMap.php
@@ -0,0 +1,237 @@
+
+
+
+
+
+
+
+
+ 컨펌스 로그인
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/composer.json b/composer.json
index 38a51e2..017baa8 100644
--- a/composer.json
+++ b/composer.json
@@ -11,6 +11,7 @@
},
"require": {
"php": "^8.1",
+ "aws/aws-sdk-php": "^3.369",
"codeigniter4/framework": "^4.0"
},
"require-dev": {
diff --git a/composer.lock b/composer.lock
index de76cfa..dae606f 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,8 +4,159 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "657ac079313fab046eb8c29fe1d6992b",
+ "content-hash": "f89d196f2c18e8021010c94fe29c716a",
"packages": [
+ {
+ "name": "aws/aws-crt-php",
+ "version": "v1.2.7",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/awslabs/aws-crt-php.git",
+ "reference": "d71d9906c7bb63a28295447ba12e74723bd3730e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/awslabs/aws-crt-php/zipball/d71d9906c7bb63a28295447ba12e74723bd3730e",
+ "reference": "d71d9906c7bb63a28295447ba12e74723bd3730e",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.5"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.8.35||^5.6.3||^9.5",
+ "yoast/phpunit-polyfills": "^1.0"
+ },
+ "suggest": {
+ "ext-awscrt": "Make sure you install awscrt native extension to use any of the functionality."
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "AWS SDK Common Runtime Team",
+ "email": "aws-sdk-common-runtime@amazon.com"
+ }
+ ],
+ "description": "AWS Common Runtime for PHP",
+ "homepage": "https://github.com/awslabs/aws-crt-php",
+ "keywords": [
+ "amazon",
+ "aws",
+ "crt",
+ "sdk"
+ ],
+ "support": {
+ "issues": "https://github.com/awslabs/aws-crt-php/issues",
+ "source": "https://github.com/awslabs/aws-crt-php/tree/v1.2.7"
+ },
+ "time": "2024-10-18T22:15:13+00:00"
+ },
+ {
+ "name": "aws/aws-sdk-php",
+ "version": "3.369.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/aws/aws-sdk-php.git",
+ "reference": "b2d04cf1184a96839a8ab62ec6e3cf2d62a278e3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/b2d04cf1184a96839a8ab62ec6e3cf2d62a278e3",
+ "reference": "b2d04cf1184a96839a8ab62ec6e3cf2d62a278e3",
+ "shasum": ""
+ },
+ "require": {
+ "aws/aws-crt-php": "^1.2.3",
+ "ext-json": "*",
+ "ext-pcre": "*",
+ "ext-simplexml": "*",
+ "guzzlehttp/guzzle": "^7.4.5",
+ "guzzlehttp/promises": "^2.0",
+ "guzzlehttp/psr7": "^2.4.5",
+ "mtdowling/jmespath.php": "^2.8.0",
+ "php": ">=8.1",
+ "psr/http-message": "^1.0 || ^2.0",
+ "symfony/filesystem": "^v6.4.3 || ^v7.1.0 || ^v8.0.0"
+ },
+ "require-dev": {
+ "andrewsville/php-token-reflection": "^1.4",
+ "aws/aws-php-sns-message-validator": "~1.0",
+ "behat/behat": "~3.0",
+ "composer/composer": "^2.7.8",
+ "dms/phpunit-arraysubset-asserts": "^0.4.0",
+ "doctrine/cache": "~1.4",
+ "ext-dom": "*",
+ "ext-openssl": "*",
+ "ext-sockets": "*",
+ "phpunit/phpunit": "^9.6",
+ "psr/cache": "^2.0 || ^3.0",
+ "psr/simple-cache": "^2.0 || ^3.0",
+ "sebastian/comparator": "^1.2.3 || ^4.0 || ^5.0",
+ "yoast/phpunit-polyfills": "^2.0"
+ },
+ "suggest": {
+ "aws/aws-php-sns-message-validator": "To validate incoming SNS notifications",
+ "doctrine/cache": "To use the DoctrineCacheAdapter",
+ "ext-curl": "To send requests using cURL",
+ "ext-openssl": "Allows working with CloudFront private distributions and verifying received SNS messages",
+ "ext-pcntl": "To use client-side monitoring",
+ "ext-sockets": "To use client-side monitoring"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.0-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/functions.php"
+ ],
+ "psr-4": {
+ "Aws\\": "src/"
+ },
+ "exclude-from-classmap": [
+ "src/data/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "Amazon Web Services",
+ "homepage": "http://aws.amazon.com"
+ }
+ ],
+ "description": "AWS SDK for PHP - Use Amazon Web Services in your PHP project",
+ "homepage": "http://aws.amazon.com/sdkforphp",
+ "keywords": [
+ "amazon",
+ "aws",
+ "cloud",
+ "dynamodb",
+ "ec2",
+ "glacier",
+ "s3",
+ "sdk"
+ ],
+ "support": {
+ "forum": "https://github.com/aws/aws-sdk-php/discussions",
+ "issues": "https://github.com/aws/aws-sdk-php/issues",
+ "source": "https://github.com/aws/aws-sdk-php/tree/3.369.1"
+ },
+ "time": "2025-12-22T19:13:21+00:00"
+ },
{
"name": "codeigniter4/framework",
"version": "v4.6.3",
@@ -80,6 +231,331 @@
},
"time": "2025-08-02T13:36:13+00:00"
},
+ {
+ "name": "guzzlehttp/guzzle",
+ "version": "7.10.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/guzzle.git",
+ "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b51ac707cfa420b7bfd4e4d5e510ba8008e822b4",
+ "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "guzzlehttp/promises": "^2.3",
+ "guzzlehttp/psr7": "^2.8",
+ "php": "^7.2.5 || ^8.0",
+ "psr/http-client": "^1.0",
+ "symfony/deprecation-contracts": "^2.2 || ^3.0"
+ },
+ "provide": {
+ "psr/http-client-implementation": "1.0"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.8.2",
+ "ext-curl": "*",
+ "guzzle/client-integration-tests": "3.0.2",
+ "php-http/message-factory": "^1.1",
+ "phpunit/phpunit": "^8.5.39 || ^9.6.20",
+ "psr/log": "^1.1 || ^2.0 || ^3.0"
+ },
+ "suggest": {
+ "ext-curl": "Required for CURL handler support",
+ "ext-intl": "Required for Internationalized Domain Name (IDN) support",
+ "psr/log": "Required for using the Log middleware"
+ },
+ "type": "library",
+ "extra": {
+ "bamarni-bin": {
+ "bin-links": true,
+ "forward-command": false
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/functions_include.php"
+ ],
+ "psr-4": {
+ "GuzzleHttp\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ },
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ },
+ {
+ "name": "Jeremy Lindblom",
+ "email": "jeremeamia@gmail.com",
+ "homepage": "https://github.com/jeremeamia"
+ },
+ {
+ "name": "George Mponos",
+ "email": "gmponos@gmail.com",
+ "homepage": "https://github.com/gmponos"
+ },
+ {
+ "name": "Tobias Nyholm",
+ "email": "tobias.nyholm@gmail.com",
+ "homepage": "https://github.com/Nyholm"
+ },
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com",
+ "homepage": "https://github.com/sagikazarmark"
+ },
+ {
+ "name": "Tobias Schultze",
+ "email": "webmaster@tubo-world.de",
+ "homepage": "https://github.com/Tobion"
+ }
+ ],
+ "description": "Guzzle is a PHP HTTP client library",
+ "keywords": [
+ "client",
+ "curl",
+ "framework",
+ "http",
+ "http client",
+ "psr-18",
+ "psr-7",
+ "rest",
+ "web service"
+ ],
+ "support": {
+ "issues": "https://github.com/guzzle/guzzle/issues",
+ "source": "https://github.com/guzzle/guzzle/tree/7.10.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/Nyholm",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-08-23T22:36:01+00:00"
+ },
+ {
+ "name": "guzzlehttp/promises",
+ "version": "2.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/promises.git",
+ "reference": "481557b130ef3790cf82b713667b43030dc9c957"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/promises/zipball/481557b130ef3790cf82b713667b43030dc9c957",
+ "reference": "481557b130ef3790cf82b713667b43030dc9c957",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2.5 || ^8.0"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.8.2",
+ "phpunit/phpunit": "^8.5.44 || ^9.6.25"
+ },
+ "type": "library",
+ "extra": {
+ "bamarni-bin": {
+ "bin-links": true,
+ "forward-command": false
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "GuzzleHttp\\Promise\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ },
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ },
+ {
+ "name": "Tobias Nyholm",
+ "email": "tobias.nyholm@gmail.com",
+ "homepage": "https://github.com/Nyholm"
+ },
+ {
+ "name": "Tobias Schultze",
+ "email": "webmaster@tubo-world.de",
+ "homepage": "https://github.com/Tobion"
+ }
+ ],
+ "description": "Guzzle promises library",
+ "keywords": [
+ "promise"
+ ],
+ "support": {
+ "issues": "https://github.com/guzzle/promises/issues",
+ "source": "https://github.com/guzzle/promises/tree/2.3.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/Nyholm",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-08-22T14:34:08+00:00"
+ },
+ {
+ "name": "guzzlehttp/psr7",
+ "version": "2.8.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/psr7.git",
+ "reference": "21dc724a0583619cd1652f673303492272778051"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/psr7/zipball/21dc724a0583619cd1652f673303492272778051",
+ "reference": "21dc724a0583619cd1652f673303492272778051",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2.5 || ^8.0",
+ "psr/http-factory": "^1.0",
+ "psr/http-message": "^1.1 || ^2.0",
+ "ralouphie/getallheaders": "^3.0"
+ },
+ "provide": {
+ "psr/http-factory-implementation": "1.0",
+ "psr/http-message-implementation": "1.0"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.8.2",
+ "http-interop/http-factory-tests": "0.9.0",
+ "phpunit/phpunit": "^8.5.44 || ^9.6.25"
+ },
+ "suggest": {
+ "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
+ },
+ "type": "library",
+ "extra": {
+ "bamarni-bin": {
+ "bin-links": true,
+ "forward-command": false
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "GuzzleHttp\\Psr7\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ },
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ },
+ {
+ "name": "George Mponos",
+ "email": "gmponos@gmail.com",
+ "homepage": "https://github.com/gmponos"
+ },
+ {
+ "name": "Tobias Nyholm",
+ "email": "tobias.nyholm@gmail.com",
+ "homepage": "https://github.com/Nyholm"
+ },
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com",
+ "homepage": "https://github.com/sagikazarmark"
+ },
+ {
+ "name": "Tobias Schultze",
+ "email": "webmaster@tubo-world.de",
+ "homepage": "https://github.com/Tobion"
+ },
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com",
+ "homepage": "https://sagikazarmark.hu"
+ }
+ ],
+ "description": "PSR-7 message implementation that also provides common utility methods",
+ "keywords": [
+ "http",
+ "message",
+ "psr-7",
+ "request",
+ "response",
+ "stream",
+ "uri",
+ "url"
+ ],
+ "support": {
+ "issues": "https://github.com/guzzle/psr7/issues",
+ "source": "https://github.com/guzzle/psr7/tree/2.8.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/Nyholm",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-08-23T21:21:41+00:00"
+ },
{
"name": "laminas/laminas-escaper",
"version": "2.18.0",
@@ -141,6 +617,232 @@
],
"time": "2025-10-14T18:31:13+00:00"
},
+ {
+ "name": "mtdowling/jmespath.php",
+ "version": "2.8.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/jmespath/jmespath.php.git",
+ "reference": "a2a865e05d5f420b50cc2f85bb78d565db12a6bc"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/a2a865e05d5f420b50cc2f85bb78d565db12a6bc",
+ "reference": "a2a865e05d5f420b50cc2f85bb78d565db12a6bc",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2.5 || ^8.0",
+ "symfony/polyfill-mbstring": "^1.17"
+ },
+ "require-dev": {
+ "composer/xdebug-handler": "^3.0.3",
+ "phpunit/phpunit": "^8.5.33"
+ },
+ "bin": [
+ "bin/jp.php"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.8-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/JmesPath.php"
+ ],
+ "psr-4": {
+ "JmesPath\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ },
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "description": "Declaratively specify how to extract elements from a JSON document",
+ "keywords": [
+ "json",
+ "jsonpath"
+ ],
+ "support": {
+ "issues": "https://github.com/jmespath/jmespath.php/issues",
+ "source": "https://github.com/jmespath/jmespath.php/tree/2.8.0"
+ },
+ "time": "2024-09-04T18:46:31+00:00"
+ },
+ {
+ "name": "psr/http-client",
+ "version": "1.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-client.git",
+ "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90",
+ "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.0 || ^8.0",
+ "psr/http-message": "^1.0 || ^2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Client\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for HTTP clients",
+ "homepage": "https://github.com/php-fig/http-client",
+ "keywords": [
+ "http",
+ "http-client",
+ "psr",
+ "psr-18"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/http-client"
+ },
+ "time": "2023-09-23T14:17:50+00:00"
+ },
+ {
+ "name": "psr/http-factory",
+ "version": "1.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-factory.git",
+ "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
+ "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1",
+ "psr/http-message": "^1.0 || ^2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Message\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories",
+ "keywords": [
+ "factory",
+ "http",
+ "message",
+ "psr",
+ "psr-17",
+ "psr-7",
+ "request",
+ "response"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/http-factory"
+ },
+ "time": "2024-04-15T12:06:14+00:00"
+ },
+ {
+ "name": "psr/http-message",
+ "version": "2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-message.git",
+ "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71",
+ "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Message\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for HTTP messages",
+ "homepage": "https://github.com/php-fig/http-message",
+ "keywords": [
+ "http",
+ "http-message",
+ "psr",
+ "psr-7",
+ "request",
+ "response"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/http-message/tree/2.0"
+ },
+ "time": "2023-04-04T09:54:51+00:00"
+ },
{
"name": "psr/log",
"version": "3.0.2",
@@ -190,6 +892,355 @@
"source": "https://github.com/php-fig/log/tree/3.0.2"
},
"time": "2024-09-11T13:17:53+00:00"
+ },
+ {
+ "name": "ralouphie/getallheaders",
+ "version": "3.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/ralouphie/getallheaders.git",
+ "reference": "120b605dfeb996808c31b6477290a714d356e822"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822",
+ "reference": "120b605dfeb996808c31b6477290a714d356e822",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.6"
+ },
+ "require-dev": {
+ "php-coveralls/php-coveralls": "^2.1",
+ "phpunit/phpunit": "^5 || ^6.5"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/getallheaders.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ralph Khattar",
+ "email": "ralph.khattar@gmail.com"
+ }
+ ],
+ "description": "A polyfill for getallheaders.",
+ "support": {
+ "issues": "https://github.com/ralouphie/getallheaders/issues",
+ "source": "https://github.com/ralouphie/getallheaders/tree/develop"
+ },
+ "time": "2019-03-08T08:55:37+00:00"
+ },
+ {
+ "name": "symfony/deprecation-contracts",
+ "version": "v3.6.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/deprecation-contracts.git",
+ "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62",
+ "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/contracts",
+ "name": "symfony/contracts"
+ },
+ "branch-alias": {
+ "dev-main": "3.6-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "function.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "A generic function and convention to trigger deprecation notices",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-09-25T14:21:43+00:00"
+ },
+ {
+ "name": "symfony/filesystem",
+ "version": "v7.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/filesystem.git",
+ "reference": "d551b38811096d0be9c4691d406991b47c0c630a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/filesystem/zipball/d551b38811096d0be9c4691d406991b47c0c630a",
+ "reference": "d551b38811096d0be9c4691d406991b47c0c630a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/polyfill-ctype": "~1.8",
+ "symfony/polyfill-mbstring": "~1.8"
+ },
+ "require-dev": {
+ "symfony/process": "^6.4|^7.0|^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Filesystem\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides basic utilities for the filesystem",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/filesystem/tree/v7.4.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-11-27T13:27:24+00:00"
+ },
+ {
+ "name": "symfony/polyfill-ctype",
+ "version": "v1.33.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-ctype.git",
+ "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638",
+ "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "provide": {
+ "ext-ctype": "*"
+ },
+ "suggest": {
+ "ext-ctype": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Ctype\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Gert de Pagter",
+ "email": "BackEndTea@gmail.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for ctype functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "ctype",
+ "polyfill",
+ "portable"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-09-09T11:45:10+00:00"
+ },
+ {
+ "name": "symfony/polyfill-mbstring",
+ "version": "v1.33.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-mbstring.git",
+ "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493",
+ "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493",
+ "shasum": ""
+ },
+ "require": {
+ "ext-iconv": "*",
+ "php": ">=7.2"
+ },
+ "provide": {
+ "ext-mbstring": "*"
+ },
+ "suggest": {
+ "ext-mbstring": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Mbstring\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for the Mbstring extension",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "mbstring",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-12-23T08:48:59+00:00"
}
],
"packages-dev": [
@@ -1980,73 +3031,6 @@
],
"time": "2023-02-07T11:34:05+00:00"
},
- {
- "name": "symfony/deprecation-contracts",
- "version": "v3.6.0",
- "source": {
- "type": "git",
- "url": "https://github.com/symfony/deprecation-contracts.git",
- "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62",
- "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62",
- "shasum": ""
- },
- "require": {
- "php": ">=8.1"
- },
- "type": "library",
- "extra": {
- "thanks": {
- "url": "https://github.com/symfony/contracts",
- "name": "symfony/contracts"
- },
- "branch-alias": {
- "dev-main": "3.6-dev"
- }
- },
- "autoload": {
- "files": [
- "function.php"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Nicolas Grekas",
- "email": "p@tchwork.com"
- },
- {
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
- }
- ],
- "description": "A generic function and convention to trigger deprecation notices",
- "homepage": "https://symfony.com",
- "support": {
- "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0"
- },
- "funding": [
- {
- "url": "https://symfony.com/sponsor",
- "type": "custom"
- },
- {
- "url": "https://github.com/fabpot",
- "type": "github"
- },
- {
- "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
- "type": "tidelift"
- }
- ],
- "time": "2024-09-25T14:21:43+00:00"
- },
{
"name": "theseer/tokenizer",
"version": "1.3.1",
diff --git a/public/plugin/img/photo.gif b/public/plugin/img/photo.gif
new file mode 100644
index 0000000..2807052
Binary files /dev/null and b/public/plugin/img/photo.gif differ
diff --git a/public/plugin/img/pin.png b/public/plugin/img/pin.png
new file mode 100644
index 0000000..05fa15c
Binary files /dev/null and b/public/plugin/img/pin.png differ