62 Commits

Author SHA1 Message Date
f8c26acea8 워커 수정 2026-04-27 15:03:36 +09:00
b0ec75ae56 트랜젝션 실패시 처리 2026-04-08 20:13:25 +09:00
a87fed538b json_encode 에러 날때 exception 처리 2026-04-07 16:03:53 +09:00
cbf7e85cf8 헤더 2026-04-07 15:58:24 +09:00
6a72ccebd5 공통화 작업 및 워커 해더 2026-04-07 15:39:41 +09:00
cba387de9d // 1. 응답 헤더 설정 (JSON)
header('Content-Type: application/json; charset=utf-8');

제거
2026-03-27 11:52:38 +09:00
930e690126 수정 2026-03-26 21:06:31 +09:00
0ef2ba77c6 현장확인 매물검증 결과관리 - 매물 입력시 제외 2026-03-26 18:17:53 +09:00
c22b023310 로그 리스트 항목 생성 2026-03-26 11:36:21 +09:00
0b6ed3df73 worker service 매물 등록 및 redis 다운시 처리 2026-03-25 20:51:38 +09:00
a170fdc774 다시 수정 2026-03-25 15:59:05 +09:00
e5a80aff33 세션 패스 수정 2026-03-25 15:52:13 +09:00
7139e0c095 세션 처리 2026-03-25 13:14:42 +09:00
129e2b4e69 왜 안되지? 2026-03-24 19:31:40 +09:00
fd187bb84a jenkins 테스트 2026-03-24 19:29:39 +09:00
3fbc57bedc jenkins 테스트 2026-03-24 19:26:39 +09:00
81b85ae25d redis connect 수정 2026-03-24 19:11:19 +09:00
e2f49ba77e redis 연결 수정 2026-03-24 18:56:01 +09:00
728b394063 api 주소 변경 2026-03-20 16:32:51 +09:00
1f445512f7 처리가능 수량관리 수정 2026-03-20 13:35:55 +09:00
b553310dc1 테스트 파일 삭제 2026-03-20 11:32:46 +09:00
bb07396abf 로그 생성이 안되어 테스트 및 수정본 2026-03-20 09:33:30 +09:00
80cb9451d2 result 입력시 usr_sq dept_sq 를 region_codes 에서 가져오는 로직 넣기 2026-03-18 16:59:16 +09:00
9138fa9c16 redis 설정 변경 2026-03-18 14:33:17 +09:00
a6ae1bd377 로그 생성 수정 2026-03-11 17:25:45 +09:00
6bb9dceec9 오류 처리 2026-03-11 15:51:17 +09:00
32efe755e6 호출시 정보 저장 2026-03-11 09:55:11 +09:00
141f526f91 수정 2026-03-10 19:38:13 +09:00
9650707caf 수정 본 2026-03-10 18:57:09 +09:00
13dfb3e112 현장확인 화면 및 기능 수정 2026-03-05 21:00:40 +09:00
9d9df394c5 현장확인매물 상세 페이지 기능 수정 2026-03-05 18:17:31 +09:00
353df045d8 Merge branch 'master' of http://192.168.10.243:3000/owrainfo/confirms 2026-03-03 21:41:09 +09:00
f02f8c0457 현장 확인 업로드 2026-03-03 21:41:02 +09:00
243ca0c45e 현장 확인 업로드 수정 2026-03-03 21:39:51 +09:00
e54b5bbbb8 1 2026-02-24 13:59:36 +09:00
69e6aa5fe6 현장확인 v2 항목 추가 2026-02-23 18:54:53 +09:00
af7846ea7c 화면 수정 2026-02-23 18:46:33 +09:00
b237c4b934 화면 수정 2026-02-20 20:47:35 +09:00
d91738c667 현장 확인 > 처리 가능 수량 관리 api 및 수정 2026-02-20 13:49:43 +09:00
c129362724 컨텐츠 영역 확장 2026-02-19 14:24:40 +09:00
49d33b55b8 좌측 메뉴 조금더 줄임 2026-02-19 14:22:35 +09:00
c3b4e0e712 화면 수정 2026-02-19 13:57:33 +09:00
f988ecabd2 701 화면 수정 2026-02-12 21:11:11 +09:00
99a072f732 오류 수정 2026-02-11 21:51:10 +09:00
b0e7cf0df0 테스트 2026-02-11 19:49:16 +09:00
ed80e90bb3 blockUI 추가 2026-02-11 19:29:44 +09:00
c17110087c blockUI success일때 추가 2026-02-11 19:23:53 +09:00
3cd6b238d2 현장확인 동기화 슬롯 api 추가 2026-02-11 18:26:48 +09:00
906f208734 수정 2026-02-10 14:17:03 +09:00
54e8c50a11 view 2026-02-10 14:13:29 +09:00
5504ca154a 워커 서비스 로그 통합 2026-02-10 11:56:01 +09:00
d666315497 주석 제거 2026-02-09 18:16:22 +09:00
424b0e9411 로그 생성 2026-02-09 18:15:45 +09:00
85fdb38281 환경별 로그 2026-02-09 18:14:22 +09:00
4f78d5493c 환경별 버킷 설정 2026-02-09 15:51:37 +09:00
56a7fe94d5 환경별 버킷 설정 수정 2026-02-09 15:50:32 +09:00
23286f6890 Merge branch 'master' of http://192.168.10.243:3000/owrainfo/confirms 2026-02-09 15:40:23 +09:00
38ea1c1129 env 환경에 따라 object storage 경로 변경 2026-02-09 15:39:52 +09:00
c6da9c12be 링크이동 수정
Reviewed-on: http://192.168.10.243:3000/owrainfo/confirms/pulls/57
2026-02-09 14:33:42 +09:00
f6857ae959 링크이동 수정
Reviewed-on: http://192.168.10.243:3000/owrainfo/confirms/pulls/56
2026-02-09 11:00:52 +09:00
98d74658e9 사이드메뉴 수정
Reviewed-on: http://192.168.10.243:3000/owrainfo/confirms/pulls/55
2026-02-09 09:49:55 +09:00
922c85962c 현장확인 수정
Reviewed-on: http://192.168.10.243:3000/owrainfo/confirms/pulls/54
2026-02-06 10:39:16 +09:00
232 changed files with 14141 additions and 9051 deletions

2
.gitignore vendored
View File

@@ -175,4 +175,4 @@ _modules/*
# 6. 기타 개인 설정 파일 (선택적) # 6. 기타 개인 설정 파일 (선택적)
.github/copilot-instructions.md .github/copilot-instructions.mdworker/fallback_queue/*.json

1
README
View File

@@ -1 +1,2 @@
여기 작업 하면 어떻게 되나? 여기 작업 하면 어떻게 되나?
1111

360
SESSION_README.md Normal file
View File

@@ -0,0 +1,360 @@
# Session 관리 가이드
## 개요
본 애플리케이션은 **Redis**를 기본 세션 저장소로 사용하며, Redis 장애 시 **Database(MariaDB)**로 자동 폴백하는 이중화 구조를 가지고 있습니다.
## 아키텍처
### 세션 저장소
| 우선순위 | 핸들러 | 설명 | 성능 |
|---------|-------|------|------|
| 1순위 | **RedisHandler** | 메모리 기반 고속 세션 | ~0.03초 |
| 2순위 | **DatabaseHandler** | DB 기반 안정적 세션 | ~0.05초 (수동 전환 시) |
### 구성 요소
```
┌─────────────────────────────────────────┐
│ 사용자 로그인 요청 │
└──────────────┬──────────────────────────┘
┌─────────────────────────────────────────┐
│ Session.php 생성자 │
│ - Redis 연결 테스트 │
│ - SESSION_FORCE_DATABASE 확인 │
└──────────────┬──────────────────────────┘
┌──────┴──────┐
│ │
Redis 정상? Redis 장애?
│ │
▼ ▼
RedisHandler DatabaseHandler
(ci_sessions (ci_sessions
테이블) 테이블)
│ │
└──────┬──────┘
세션 데이터 저장/조회
```
## 설정 파일
### 1. Session.php (`src/app/Config/Session.php`)
```php
public string $driver = RedisHandler::class;
public function __construct()
{
// 환경변수로 강제 Database 모드 설정 가능
$forceDatabase = env('SESSION_FORCE_DATABASE', false);
if ($this->driver === RedisHandler::class && !$forceDatabase) {
try {
// Redis 연결 테스트 (타임아웃: 0.5초)
$testRedis = get_redis_connection('session');
if (!$testRedis) {
throw new \Exception('Redis connection failed');
}
// Redis 정상 - Redis 사용
$this->savePath = 'tcp://192.168.10.243:6379?database=0';
} catch (\Exception $e) {
// Redis 실패 - DatabaseHandler로 폴백
$this->driver = DatabaseHandler::class;
$this->savePath = 'ci_sessions';
}
} else {
// Database 모드
$this->savePath = 'ci_sessions';
}
}
```
### 2. 환경 설정 (`.env`)
```bash
# Redis 세션 설정
SESSION_REDIS_HOST = 192.168.10.243
SESSION_REDIS_PORT = 6379
SESSION_REDIS_DATABASE = 0
#SESSION_REDIS_PASSWORD =
# 세션 강제 Database 모드 (Redis 장애 시 true로 변경)
SESSION_FORCE_DATABASE = false
```
### 3. Redis Helper (`src/app/Helpers/redis_helper.php`)
```php
function get_redis_connection(string $type = 'worker')
{
$redis = new \Redis();
// 타임아웃 0.5초로 빠른 실패 감지
$timeout = 0.5;
$success = $redis->connect($host, $port, $timeout);
return $success ? $redis : false;
}
```
## 데이터베이스 테이블
### ci_sessions 테이블 구조
```sql
CREATE TABLE `ci_sessions` (
`session_id` VARCHAR(40) NOT NULL DEFAULT '0',
`ip_address` VARCHAR(16) NOT NULL DEFAULT '0',
`user_agent` VARCHAR(120) NOT NULL,
`last_activity` INT UNSIGNED NOT NULL DEFAULT 0,
`user_data` VARCHAR(4000) NOT NULL,
PRIMARY KEY (`session_id`)
) ENGINE = MEMORY;
CREATE INDEX `last_activity_idx`
ON `ci_sessions` (`last_activity` ASC);
```
**주요 특징:**
- **ENGINE = MEMORY**: 메모리 기반 테이블로 매우 빠른 읽기/쓰기 성능
- **장점**: Redis와 비슷한 속도 (디스크 I/O 없음)
- **단점**: 서버 재시작 시 세션 데이터 소실 (일반적으로 문제없음 - 세션은 휘발성 데이터)
- **용량**: VARCHAR(4000)으로 충분한 세션 데이터 저장 가능
## 운영 가이드
### 1. Redis 장애 발생 시
#### 자동 폴백 (기본 동작)
- Redis 연결 실패 시 자동으로 DatabaseHandler로 전환
- **단점**: 매 요청마다 Redis 연결 시도 → 타임아웃 대기 (약 0.5초 추가)
- 사용자는 약간 느린 응답 시간을 경험
#### 수동 전환 (권장)
Redis 장애 감지 즉시 다음 조치:
```bash
# 1. .env 파일 수정
vi /path/to/src/.env
# SESSION_FORCE_DATABASE를 true로 변경
SESSION_FORCE_DATABASE = true
# 2. PHP-FPM 재시작 (선택사항, 다음 요청부터 자동 적용)
docker exec projects-admin_confirms kill -USR2 1
```
**장점**: Redis 연결 시도 없이 즉시 Database 사용 → 빠른 응답 (0.05초)
### 2. Redis 복구 후
```bash
# 1. .env 파일 원복
SESSION_FORCE_DATABASE = false
# 2. PHP-FPM 재시작
docker exec projects-admin_confirms kill -USR2 1
```
### 3. 모니터링
#### 로그 확인
```bash
# 세션 관련 로그 확인
docker exec projects-admin_confirms tail -f /var/www/html/writable/logs/log-$(date +%Y-%m-%d).log | grep -i session
# Redis 폴백 경고 확인
docker exec projects-admin_confirms grep "Redis unavailable" /var/www/html/writable/logs/log-$(date +%Y-%m-%d).log
```
#### 예상 로그 메시지
**Redis 정상:**
```
DEBUG - Session: Class initialized using 'CodeIgniter\Session\Handlers\RedisHandler' driver.
```
**Redis 장애 (자동 폴백):**
```
WARNING - Session: Redis unavailable (Redis connection failed), falling back to DatabaseHandler
DEBUG - Session: Class initialized using 'CodeIgniter\Session\Handlers\DatabaseHandler' driver.
```
**Database 강제 모드:**
```
INFO - Session: Forced to use DatabaseHandler (SESSION_FORCE_DATABASE=true)
DEBUG - Session: Class initialized using 'CodeIgniter\Session\Handlers\DatabaseHandler' driver.
```
### 4. 성능 모니터링
```bash
# 로그인 응답 시간 측정
for i in {1..5}; do
curl -X POST http://localtest2-admin.confirms.co.kr/login/chkLogin \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "user_id=test&user_pw=test" \
-o /dev/null -s -w " Time: %{time_total}s\n"
done
```
**기대 성능:**
- Redis 정상: 0.03 ~ 0.05초
- Database (수동 전환): 0.05 ~ 0.08초
- 자동 폴백 (Redis 다운): 0.5 ~ 1.0초
## 사용자 알림
### 프론트엔드 경고 표시
Redis 장애 시 로그인 페이지에 자동으로 경고 배너가 표시됩니다:
```javascript
// login.php의 JavaScript
function checkSystemStatus(responseData) {
if (responseData.system && responseData.system.redis_fallback) {
// 경고 메시지: "세션 서버(Redis) 장애로 임시 모드로 운영 중입니다."
showRedisWarning(responseData.system.warning_message);
}
}
```
**API 응답 예시:**
```json
{
"code": "0",
"msg": "success",
"system": {
"redis_fallback": true,
"warning_message": "세션 서버(Redis) 장애로 임시 모드로 운영 중입니다. 시스템 관리자에게 문의하세요."
}
}
```
## 트러블슈팅
### 문제: 로그인이 느림 (2~3초)
**원인**: Redis 다운 상태에서 자동 폴백 모드 사용 중
**해결**:
```bash
# SESSION_FORCE_DATABASE=true로 수동 전환
echo "SESSION_FORCE_DATABASE = true" >> /path/to/src/.env
```
### 문제: 세션이 유지되지 않음
**원인**: ci_sessions 테이블이 없거나 권한 문제
**해결**:
```sql
-- 테이블 존재 확인
SHOW TABLES LIKE 'ci_sessions';
-- 권한 확인
SHOW GRANTS FOR 'confirms'@'%';
-- 테이블이 없는 경우 생성
CREATE TABLE `ci_sessions` (
`session_id` VARCHAR(40) NOT NULL DEFAULT '0',
`ip_address` VARCHAR(16) NOT NULL DEFAULT '0',
`user_agent` VARCHAR(120) NOT NULL,
`last_activity` INT UNSIGNED NOT NULL DEFAULT 0,
`user_data` VARCHAR(4000) NOT NULL,
PRIMARY KEY (`session_id`)
) ENGINE = MEMORY;
CREATE INDEX `last_activity_idx`
ON `ci_sessions` (`last_activity` ASC);
```
### 문제: 서버 재시작 후 모든 세션이 사라짐
**원인**: ci_sessions가 MEMORY 엔진을 사용 (정상 동작)
**설명**:
- MEMORY 엔진은 서버 재시작 시 데이터가 삭제됩니다
- 세션은 일시적 데이터이므로 일반적으로 문제가 되지 않습니다
- 사용자는 재로그인하면 됩니다
**영구 저장이 필요한 경우**:
```sql
-- InnoDB로 변경 (성능은 약간 느려짐)
ALTER TABLE ci_sessions ENGINE=InnoDB;
```
### 문제: Redis 연결 타임아웃이 너무 김
**원인**: redis_helper.php의 타임아웃 설정
**해결**:
```php
// src/app/Helpers/redis_helper.php
$timeout = 0.5; // 0.1 ~ 1.0 사이 값으로 조정
$redis->connect($host, $port, $timeout);
```
## 관련 파일
| 파일 | 역할 |
|------|------|
| `src/app/Config/Session.php` | 세션 설정 및 폴백 로직 |
| `src/app/Helpers/redis_helper.php` | Redis 연결 헬퍼 함수 |
| `src/app/Controllers/Login.php` | 로그인 및 시스템 상태 체크 |
| `src/app/Views/pages/login.php` | 프론트엔드 경고 표시 |
| `src/.env` | 환경 설정 (Redis 연결 정보, 강제 모드) |
## Worker 및 API (참고)
### Worker (NaverWorker.php)
- Redis 큐 사용 (DB 9)
- Redis 장애 시 파일 기반 폴백 (`worker/fallback_queue/*.json`)
### API (api_receiver.php, KisoController.php)
- Redis 큐 사용 (DB 9)
- Redis 장애 시 파일 기반 폴백 (`worker/fallback_queue/*.json`)
## 추가 개선 사항
### 고려 중인 기능
1. **Redis Sentinel 도입** (고가용성)
- 자동 장애 조치
- 마스터/슬레이브 구조
2. **APM 연동**
- New Relic, DataDog 등
- 실시간 성능 모니터링
3. **헬스체크 엔드포인트**
```php
// GET /health/session
{
"status": "ok",
"driver": "RedisHandler",
"redis_available": true,
"response_time_ms": 12
}
```
## 문의
세션 관련 문제 발생 시:
1. 로그 확인 (`/writable/logs/`)
2. Redis 서버 상태 확인
3. ci_sessions 테이블 상태 확인
4. 필요 시 수동 전환 수행
---
**Last Updated**: 2026-03-25
**Version**: 1.0.0

209
app/Commands/NaverRetry.php Normal file
View File

@@ -0,0 +1,209 @@
<?php
namespace App\Commands;
use CodeIgniter\CLI\BaseCommand;
use CodeIgniter\CLI\CLI;
use App\Models\Entities\NaverWorkerLogModel;
class NaverRetry extends BaseCommand
{
protected $group = 'Workers';
protected $name = 'naver:retry';
protected $description = '실패한 Naver Worker 작업을 재처리합니다.';
protected $usage = 'naver:retry [log_id] [options]';
protected $arguments = [
'log_id' => '재처리할 특정 로그 ID (선택사항, 없으면 모든 실패 건 재처리)'
];
protected $options = [
'--limit' => '재처리할 최대 개수 (기본: 10)',
'--dry-run' => '실제 실행 없이 목록만 확인',
'--force' => '재시도 횟수 제한 무시 (3회 이상 실패한 건도 재처리)'
];
public function run(array $params)
{
helper(['log']);
$logId = $params[0] ?? null;
$limit = CLI::getOption('limit') ?? 10;
$isDryRun = CLI::getOption('dry-run') !== null;
$isForce = CLI::getOption('force') !== null;
$logModel = model(NaverWorkerLogModel::class);
$naverService = new \App\Services\NaverService();
// 1. 특정 ID 재처리
if ($logId) {
$this->retryOne($logModel, $naverService, $logId, $isDryRun, $isForce);
return;
}
// 2. 실패한 모든 작업 재처리
$this->retryFailed($logModel, $naverService, $limit, $isDryRun, $isForce);
}
/**
* 특정 로그 ID 재처리
*/
protected function retryOne($logModel, $naverService, $logId, $isDryRun, $isForce)
{
$log = $logModel->find($logId);
if (!$log) {
CLI::error("❌ Log ID {$logId} not found");
return;
}
CLI::write(CLI::color("📋 Log ID: {$logId}", 'cyan'));
CLI::write(" Status: {$log['status']}");
CLI::write(" Retry Count: " . ($log['retry_cnt'] ?? 0));
CLI::write(" Error: {$log['error_msg']}");
CLI::write(" Created: {$log['created_at']}");
// 재시도 횟수 체크
if (!$isForce && ($log['retry_cnt'] ?? 0) >= 3) {
CLI::write(CLI::color('⚠️ 이미 3회 이상 재시도했습니다. --force 옵션으로 강제 실행 가능', 'yellow'));
return;
}
// 재시도 불가능한 오류 체크
if (!$isForce && $this->isNonRetryableError($log['error_msg'])) {
CLI::write(CLI::color('⚠️ 재시도 불가능한 오류입니다. 데이터를 먼저 수정해주세요.', 'yellow'));
CLI::write(CLI::color(' (--force 옵션으로 강제 실행 가능)', 'yellow'));
return;
}
if ($isDryRun) {
CLI::write(CLI::color('🔍 DRY RUN - 재처리하지 않음', 'yellow'));
return;
}
$this->processLog($logModel, $naverService, $log);
}
/**
* 재시도 불가능한 오류인지 확인
*/
protected function isNonRetryableError($errorMsg)
{
if (!$errorMsg) return false;
// 데이터 수정이 필요한 오류 패턴
$nonRetryablePatterns = [
'foreign key constraint',
'Duplicate entry',
'빈 페이로드',
'usr_sq.*users 테이블에 없음', // 이미 폴백 적용된 건은 재시도 가능
];
foreach ($nonRetryablePatterns as $pattern) {
if (stripos($errorMsg, $pattern) !== false) {
return true;
}
}
return false;
}
/**
* 실패한 모든 작업 재처리
*/
protected function retryFailed($logModel, $naverService, $limit, $isDryRun, $isForce)
{
$query = $logModel->where('status', 'FAIL');
// force가 아닌 경우 재시도 횟수 3회 미만만 조회
if (!$isForce) {
$query->groupStart()
->where('retry_cnt IS NULL')
->orWhere('retry_cnt <', 3)
->groupEnd();
}
$failedLogs = $query->orderBy('created_at', 'ASC')->findAll($limit);
if (empty($failedLogs)) {
CLI::write(CLI::color('✅ 재처리할 실패 작업이 없습니다.', 'green'));
return;
}
CLI::write(CLI::color("📋 실패한 작업 {count}건 발견", 'cyan')
->replace('{count}', count($failedLogs)));
if ($isDryRun) {
foreach ($failedLogs as $log) {
CLI::write(" - ID: {$log['seq']} | Atcl: {$log['atcl_no']} | Error: {$log['error_msg']}");
}
CLI::write(CLI::color('🔍 DRY RUN - 재처리하지 않음', 'yellow'));
return;
}
// 실제 재처리
$successCount = 0;
$failCount = 0;
foreach ($failedLogs as $log) {
CLI::write(CLI::color("\n🔄 Retrying Log ID: {$log['seq']}", 'yellow'));
$result = $this->processLog($logModel, $naverService, $log);
if ($result) {
$successCount++;
} else {
$failCount++;
}
// 과부하 방지
sleep(1);
}
CLI::write(CLI::color("\n✅ 재처리 완료: 성공 {$successCount}건, 실패 {$failCount}", 'green'));
}
/**
* 로그 데이터 재처리
*/
protected function processLog($logModel, $naverService, $log)
{
try {
// 상태를 RETRY로 변경
$logModel->update($log['seq'], ['status' => 'RETRY']);
// JSON 파싱
$responseJson = json_decode($log['raw_payload'], true);
$payload = $responseJson['request_data'] ?? [];
if (empty($payload)) {
throw new \Exception("빈 페이로드 데이터");
}
CLI::write(" Article: {$payload['articleNumber']}");
// 재처리
$insertId = $naverService->processArticle($payload);
// 성공 시 로그 업데이트
$logModel->update($log['seq'], [
'atcl_no' => $payload['articleNumber'] ?? null,
'status' => 'SUCCESS',
'target_db_id' => $insertId,
'error_msg' => null
]);
CLI::write(CLI::color(" ✅ Success! DB ID: {$insertId}", 'green'));
return true;
} catch (\Exception $e) {
// 실패 시 로그 업데이트
$logModel->update($log['seq'], [
'status' => 'FAIL',
'error_msg' => $e->getMessage()
]);
CLI::error(" ❌ Failed: " . $e->getMessage());
return false;
}
}
}

View File

@@ -17,57 +17,125 @@ class NaverWorker extends BaseCommand
// DB 객체를 담을 변수 선언 // DB 객체를 담을 변수 선언
protected $db; protected $db;
protected $redisHost;
protected $redisPort;
protected $redisDatabase;
public function run(array $params) public function run(array $params)
{ {
helper('log'); // 여기서 로드 완료! helper(['log', 'redis']); // redis helper 추가
$this->db = \Config\Database::connect(); $this->db = \Config\Database::connect();
// 워커 시작 시점에 선제적으로 연결 상태를 보정
try {
$this->db->initialize();
} catch (\Throwable $e) {
CLI::error('Database connection init failed: ' . $e->getMessage());
}
$logModel = model(NaverWorkerLogModel::class); $logModel = model(NaverWorkerLogModel::class);
$naverService = new \App\Services\NaverService(); // 서비스 생성 $naverService = new \App\Services\NaverService(); // 서비스 생성
$redis = new \Redis(); // Redis 연결 (실패해도 계속 진행 - 파일 모드로 동작 가능)
try { $redis = get_redis_connection('worker');
$redis->connect('redis', 6379); $config = get_redis_config('worker');
$redis->select(9);
CLI::write(CLI::color('🟢 Naver Worker running...', 'green')); if ($redis) {
} catch (\Exception $e) { CLI::write(CLI::color('🟢 Naver Worker running... (Redis: ' . $config['host'] . ':' . $config['port'] . ' DB:' . $config['database'] . ')', 'green'));
CLI::error("Redis 연결 불가: " . $e->getMessage()); } else {
return; CLI::write(CLI::color('⚠️ Naver Worker running in FILE-ONLY mode (Redis unavailable)', 'yellow'));
} }
while (true) { while (true) {
// 1. DB 연결 상태 체크 (더 견고하게) // Redis 또는 폴백 파일에서 데이터 읽기
$rawData = null;
$source = 'redis'; // 데이터 소스 추적
// 1. Redis에서 데이터 읽기 시도 (Redis가 있을 경우만)
if ($redis) {
$maxRetries = 2;
$retryCount = 0;
while ($retryCount < $maxRetries) {
try { try {
if ($this->db->connID === false || !@$this->db->connID->ping()) { $result = $redis->brPop(['naver:raw_queue'], 5); // 5초 타임아웃
$this->db->reconnect(); if ($result) {
CLI::write(CLI::color('🔄 Database reconnected.', 'yellow')); $rawData = $result[1];
$source = 'redis';
} }
} catch (\Throwable $e) { break; // 성공하면 루프 탈출
$this->db->reconnect(); } catch (\Exception $e) {
$retryCount++;
CLI::write(CLI::color("⚠️ Redis error (attempt {$retryCount}/{$maxRetries}): " . $e->getMessage(), 'yellow'));
if ($retryCount >= $maxRetries) {
CLI::write(CLI::color("⚠️ Redis unavailable, switching to file mode", 'yellow'));
$redis = null; // Redis를 비활성화
break;
} }
$result = $redis->brPop(['naver:raw_queue'], 30); // Redis 재연결 시도
try {
CLI::write(CLI::color('🔄 Reconnecting to Redis...', 'yellow'));
$redis->close();
$redis = get_redis_connection('worker');
if ($redis) {
CLI::write(CLI::color('✅ Redis reconnected', 'green'));
}
} catch (\Exception $reconnectError) {
CLI::error("Redis reconnection error: " . $reconnectError->getMessage());
}
}
}
}
if (!$result) { // 2. Redis에서 데이터 없으면 폴백 파일 확인
// 데이터가 없어서 타임아웃 난 경우. if (!$rawData) {
// 굳이 sleep 안 해도 바로 다음 brPop이 다시 30초 대기를 시작함. $rawData = $this->readFromFallbackFile();
if ($rawData) {
$source = 'file';
}
}
// 3. 데이터 없으면 다음 루프
if (!$rawData) {
continue; continue;
} }
if ($result) { // 4. 데이터 소스 로깅
$rawData = $result[1]; CLI::write(CLI::color("📥 Data received from: " . strtoupper($source), 'cyan'));
// [1] 꺼내자마자 DB에 원문 저장 (2차 임시 저장)
// [1] 꺼내자마자 DB에 원문 저장 (2차 임시 저장) - 실패 시 재시도
try {
$logId = $logModel->insert([ $logId = $logModel->insert([
'raw_payload' => $rawData, 'raw_payload' => $rawData,
'status' => 'INIT' 'status' => 'INIT'
]); ]);
} catch (\Throwable $e) {
// MySQL 연결 계열 에러 시 재연결 후 재시도
if ($this->isMySqlConnectionError($e)) {
CLI::write(CLI::color('⚠️ MySQL gone away, reconnecting...', 'yellow'));
$this->db->close();
$this->db = \Config\Database::connect();
$this->db->initialize();
$logModel = model(NaverWorkerLogModel::class);
// 재시도
$logId = $logModel->insert([
'raw_payload' => $rawData,
'status' => 'INIT'
]);
} else {
throw $e; // 다른 에러면 그대로 throw
}
}
try { try {
$responseJson = json_decode($result[1], true); $responseJson = json_decode($rawData, true);
$payload = $responseJson['request_data'] ?? []; $payload = $responseJson['request_data'] ?? [];
if (empty($payload)) { if (empty($payload)) {
@@ -77,23 +145,48 @@ class NaverWorker extends BaseCommand
// 서비스의 함수 하나로 모든 처리 완료 // 서비스의 함수 하나로 모든 처리 완료
$insertId = $naverService->processArticle($payload); $insertId = $naverService->processArticle($payload);
// [3] 성공 시 로그 업데이트 // [3] 성공 시 로그 업데이트 (재연결 처리 포함)
$logModel->update($logId, [ $this->safeUpdateLog($logModel, $logId, [
'atcl_no' => $payload['articleNumber'] ?? null, 'atcl_no' => $payload['articleNumber'] ?? null,
'status' => 'SUCCESS', 'status' => 'SUCCESS',
'target_db_id' => $insertId 'target_db_id' => $insertId
]); ]);
CLI::write("✅ Success! DB ID: $insertId", 'cyan'); CLI::write("✅ Success! DB ID: $insertId | Source: $source", 'cyan');
} catch (\Exception $e) { } catch (\Exception $e) {
CLI::error("❌ Task Failed: " . $e->getMessage()); CLI::error("❌ Task Failed: " . $e->getMessage());
// 실패 로그는 여기서 남김
// 1. DB 상태를 FAIL로 업데이트 (필수)
$logModel->update($logId, ['status' => 'FAIL', 'error_msg' => $e->getMessage()]);
// 2. Redis 실패 큐에 백업 (선택 - 나중에 모아서 다시 던질 때 편함) // payload에서 매물번호 추출 시도
$atclNo = null;
try {
if (!empty($rawData)) {
$responseJson = json_decode($rawData, true);
$payload = $responseJson['request_data'] ?? [];
$atclNo = $payload['articleNumber'] ?? null;
}
} catch (\Exception $parseEx) {
// JSON 파싱 실패는 무시
}
// 실패 로그는 여기서 남김
// 1. DB 상태를 FAIL로 업데이트 (필수) (재연결 처리 포함)
$this->safeUpdateLog($logModel, $logId, [
'atcl_no' => $atclNo,
'status' => 'FAIL',
'error_msg' => $e->getMessage()
]);
// 2. Redis 실패 큐에 백업 (선택 - Redis가 있을 경우만)
if ($redis) {
try {
$redis->lPush('naver:failed_queue', $rawData); $redis->lPush('naver:failed_queue', $rawData);
} catch (\Exception $redisEx) {
// Redis 실패 시에도 에러 처리하지 않음 (이미 DB에 FAIL 로그 남김)
CLI::write(CLI::color('⚠️ Failed to push to failed_queue: ' . $redisEx->getMessage(), 'yellow'));
}
}
helper('log'); helper('log');
write_custom_log("FAILED_DATA | Error: " . $e->getMessage(), 'ERROR', 'failed'); write_custom_log("FAILED_DATA | Error: " . $e->getMessage(), 'ERROR', 'failed');
@@ -101,6 +194,103 @@ class NaverWorker extends BaseCommand
sleep(1); sleep(1);
} }
} }
}
/**
* MySQL 연결 계열 에러 발생 시 재연결 후 재시도하는 안전한 update
*/
protected function safeUpdateLog($logModel, $logId, $data)
{
try {
return $logModel->update($logId, $data);
} catch (\Throwable $e) {
if ($this->isMySqlConnectionError($e)) {
CLI::write(CLI::color('⚠️ MySQL gone away on update, reconnecting...', 'yellow'));
$this->db->close();
$this->db = \Config\Database::connect();
$this->db->initialize();
$logModel = model(\App\Models\Entities\NaverWorkerLogModel::class);
// 재시도
return $logModel->update($logId, $data);
} else {
throw $e;
}
}
}
/**
* MySQL 연결 끊김 계열 에러 여부 판별
*/
protected function isMySqlConnectionError(\Throwable $e): bool
{
$message = strtolower($e->getMessage());
return str_contains($message, 'mysql server has gone away')
|| str_contains($message, 'lost connection to mysql server')
|| str_contains($message, 'server has gone away');
}
/**
* 폴백 파일에서 데이터 읽기 (Redis 장애 시 파일에서 직접 처리)
*
* @return string|null JSON 데이터 또는 null
*/
protected function readFromFallbackFile()
{
$fallbackDir = ROOTPATH . 'worker/fallback_queue';
// 폴백 디렉토리가 없으면 null 반환
if (!is_dir($fallbackDir)) {
return null;
}
// 폴백 파일 목록 가져오기 (오래된 순서대로)
$files = glob($fallbackDir . '/*.json');
if (empty($files)) {
return null;
}
sort($files); // 파일명(타임스탬프) 기준 정렬
// 가장 오래된 파일 하나 처리
$filePath = $files[0];
try {
// 파일 락을 사용하여 읽기 (동시 접근 방지)
$fp = fopen($filePath, 'r');
if (!$fp) {
CLI::write(CLI::color("⚠️ Failed to open fallback file: " . basename($filePath), 'yellow'));
return null;
}
// 배타적 락 획득 시도
if (!flock($fp, LOCK_EX | LOCK_NB)) {
// 락 획득 실패 (다른 프로세스가 처리 중)
fclose($fp);
return null;
}
// 파일 내용 읽기
$content = stream_get_contents($fp);
// 파일 삭제 (처리 완료로 간주)
flock($fp, LOCK_UN);
fclose($fp);
unlink($filePath);
CLI::write(CLI::color("📂 Processing fallback file: " . basename($filePath), 'green'));
return $content;
} catch (\Exception $e) {
CLI::write(CLI::color("❌ Error reading fallback file " . basename($filePath) . ": " . $e->getMessage(), 'red'));
if (isset($fp) && is_resource($fp)) {
flock($fp, LOCK_UN);
fclose($fp);
}
return null;
} }
} }

32
app/Commands/TestLog.php Normal file
View File

@@ -0,0 +1,32 @@
<?php
namespace App\Commands;
use CodeIgniter\CLI\BaseCommand;
use CodeIgniter\CLI\CLI;
class TestLog extends BaseCommand
{
protected $group = 'Test';
protected $name = 'test:log';
protected $description = 'Test log_message function';
public function run(array $params)
{
CLI::write('Testing log_message()...', 'yellow');
log_message('error', '===== TEST LOG MESSAGE FROM CLI ===== ' . date('Y-m-d H:i:s'));
log_message('debug', 'Debug level test');
log_message('info', 'Info level test');
$logFile = WRITEPATH . 'logs/log-' . date('Y-m-d') . '.log';
CLI::write('Log file: ' . $logFile);
CLI::write('Exists: ' . (file_exists($logFile) ? 'YES' : 'NO'));
if (file_exists($logFile)) {
CLI::write('Last 10 lines:');
CLI::write(shell_exec('tail -10 ' . escapeshellarg($logFile)));
}
}
}

View File

@@ -16,7 +16,7 @@ class App extends BaseConfig
* *
* E.g., http://example.com/ * E.g., http://example.com/
*/ */
public string $baseURL = 'http://test2-admin.confirms.co.kr'; public string $baseURL = 'http://localtest2-admin.confirms.co.kr';
/** /**
* Allowed Hostnames in the Site URL other than the hostname in the baseURL. * Allowed Hostnames in the Site URL other than the hostname in the baseURL.
@@ -199,4 +199,37 @@ class App extends BaseConfig
* @see http://www.w3.org/TR/CSP/ * @see http://www.w3.org/TR/CSP/
*/ */
public bool $CSPEnabled = false; public bool $CSPEnabled = false;
/**
* --------------------------------------------------------------------------
* Constructor - .env 값 로드
* --------------------------------------------------------------------------
*/
public function __construct()
{
parent::__construct();
// .env 파일의 값으로 속성 오버라이드
if (env('app.baseURL')) {
$this->baseURL = env('app.baseURL');
}
if (env('app.appTimezone')) {
$this->appTimezone = env('app.appTimezone');
}
if (env('app.forceGlobalSecureRequests') !== null) {
$this->forceGlobalSecureRequests = filter_var(
env('app.forceGlobalSecureRequests'),
FILTER_VALIDATE_BOOLEAN
);
}
if (env('app.CSPEnabled') !== null) {
$this->CSPEnabled = filter_var(
env('app.CSPEnabled'),
FILTER_VALIDATE_BOOLEAN
);
}
}
} }

View File

@@ -159,13 +159,13 @@ class Cache extends BaseConfig
{ {
parent::__construct(); parent::__construct();
// Redis 설정에 .env 값을 할당 (이전 논의된 Docker 호스트 이름 'redis' 사용) // Redis 설정에 환경 변수 값을 할당 (두 가지 환경 변수 형식 지원)
$this->redis = [ $this->redis = [
'host' => env('redis.default.host', '127.0.0.1'), 'host' => env('REDIS_HOST', env('redis.default.host', '127.0.0.1')),
'password' => (env('redis.default.password') === '' || env('redis.default.password') === null) ? null : env('redis.default.password'), 'password' => env('REDIS_PASSWORD', env('redis.default.password')) ?: null,
'port' => (int)env('redis.default.port', 6379), 'port' => (int)(env('REDIS_PORT', env('redis.default.port', 6379))),
'timeout' => (int)env('redis.default.timeout', 0), 'timeout' => (int)(env('REDIS_TIMEOUT', env('redis.default.timeout', 0))),
'database' => (int)env('redis.default.database', 0) 'database' => (int)(env('REDIS_DATABASE', env('redis.default.database', 0)))
]; ];
// 필요하다면, 이 생성자에서 $handler나 $backupHandler 같은 다른 설정도 // 필요하다면, 이 생성자에서 $handler나 $backupHandler 같은 다른 설정도

View File

@@ -82,8 +82,18 @@ defined('EXIT__AUTO_MAX') || define('EXIT__AUTO_MAX', 125); // highest automa
/** /**
* ncloud url * ncloud url
*/ */
define('NCLOUD_OBJECT_STORAGE_URL', 'https://kr.object.ncloudstorage.com/confirms-object'); // 환경별 버킷 설정
define('NCLOUD_S3_KEY', 'ncp_iam_BPAMKR3l50hXJiQ6qpSP'); $environment = getenv('CI_ENVIRONMENT') ?: 'production';
define('NCLOUD_S3_SECRET', 'ncp_iam_BPKMKRW2GU59UE59I1QftVGst6NJgnmbSc');
define('NCLOUD_S3_BUCKET', 'confirms-object'); if ($environment === 'development') {
define('NCLOUD_S3_ENDPOINT', 'https://kr.object.ncloudstorage.com'); define('NCLOUD_S3_BUCKET', 'confirms-object-test');
define('NCLOUD_OBJECT_STORAGE_URL', 'https://kr.object.ncloudstorage.com/confirms-object-test');
} else {
define('NCLOUD_S3_BUCKET', 'confirms-object');
define('NCLOUD_OBJECT_STORAGE_URL', 'https://kr.object.ncloudstorage.com/confirms-object');
}
// .env 파일에서 NCLOUD 설정 읽기
define('NCLOUD_S3_KEY', getenv('ncloud.s3.key') ?: 'ncp_iam_BPAMKR3l50hXJiQ6qpSP');
define('NCLOUD_S3_SECRET', getenv('ncloud.s3.secret') ?: 'ncp_iam_BPKMKRW2GU59UE59I1QftVGst6NJgnmbSc');
define('NCLOUD_S3_ENDPOINT', getenv('ncloud.s3.endpoint') ?: 'https://kr.object.ncloudstorage.com');

View File

@@ -39,7 +39,7 @@ class Logger extends BaseConfig
* *
* @var int|list<int> * @var int|list<int>
*/ */
public $threshold = (ENVIRONMENT === 'production') ? 4 : 9; public $threshold = 9; // Always log everything for debugging
/** /**
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------

View File

@@ -28,6 +28,8 @@ $routes->group('common', ['namespace' => 'App\Controllers\Common'], function ($r
$routes->post('common/changeUserPass', 'Common::changeUserPass'); // 비밀번호변경 $routes->post('common/changeUserPass', 'Common::changeUserPass'); // 비밀번호변경
$routes->get('getComplexList', 'Common::getComplexList'); // 단지목록조회
$routes->get('getPyeongInfo', 'Common::getPyeongInfo'); // 평형정보조회
}); });
@@ -76,10 +78,16 @@ $routes->group('', ['namespace' => 'App\Controllers\Article'], static function (
$routes->post('chgStatus', 'Receipt::chgStatus'); // 상태변경 $routes->post('chgStatus', 'Receipt::chgStatus'); // 상태변경
$routes->post('sendSms', 'Receipt::sendSms'); // 문자발송 $routes->post('sendSms', 'Receipt::sendSms'); // 문자발송
$routes->post('saveRecInfo', 'Receipt::saveRecInfo'); // 거주인정보저장 $routes->post('saveRecInfo', 'Receipt::saveRecInfo'); // 거주인정보저장
$routes->get('getRecInfo', 'Receipt::getRecInfo'); // 거주인정보조회
$routes->get('getHistory', 'Receipt::getHistory'); // 정보변경이력조회
$routes->get('getImages', 'Receipt::getImages'); // 이미지 목록 조회
$routes->post('uploadFile', 'Receipt::uploadFile'); // 파일업로드 $routes->post('uploadFile', 'Receipt::uploadFile'); // 파일업로드
$routes->post('removeUploadFile', 'Receipt::removeUploadFile'); // 파일삭제 $routes->post('removeUploadFile', 'Receipt::removeUploadFile'); // 파일삭제
$routes->post('updateImageOrder', 'Receipt::updateImageOrder'); // 이미지 순서 업데이트
$routes->get('downloadAllImages', 'Receipt::downloadAllImages'); // 이미지 일괄 다운로드 $routes->get('downloadAllImages', 'Receipt::downloadAllImages'); // 이미지 일괄 다운로드
$routes->post('saveImgLocation', 'Receipt::saveImgLocation'); // 촬영위치 저장 $routes->post('saveImgLocation', 'Receipt::saveImgLocation'); // 촬영위치 저장
$routes->post('modifyPriceInfo', 'Receipt::modifyPriceInfo'); // 가격정보 수정
}); });
/** /**
@@ -661,6 +669,12 @@ $routes->group('manage', ['namespace' => 'App\Controllers\Manage'], function ($r
/** API - 로그인로그관리 */ /** API - 로그인로그관리 */
$routes->get('loginlog/getLogList', 'LoginLog::getLogList'); $routes->get('loginlog/getLogList', 'LoginLog::getLogList');
$routes->get('loginlog/excel', 'LoginLog::excel'); $routes->get('loginlog/excel', 'LoginLog::excel');
/** Worker 로그 관리 */
$routes->get('workerlog', 'WorkerLog::index');
$routes->get('workerlog/stream', 'WorkerLog::stream');
$routes->get('workerlog/download', 'WorkerLog::download');
$routes->post('workerlog/delete', 'WorkerLog::delete');
}); });
/** /**
@@ -687,3 +701,12 @@ $routes->post('/login/chkLogin', 'Login::chkLogin');
if (is_file($filepath = APPPATH . 'Config/Routes/Api.php')) { if (is_file($filepath = APPPATH . 'Config/Routes/Api.php')) {
require $filepath; require $filepath;
} }
/**
* Worker 관리
*/
$routes->group('manage/worker', ['namespace' => 'App\Controllers\Manage'], function ($routes) {
$routes->get('failed', 'WorkerLogController::failedList');
$routes->post('retry', 'WorkerLogController::retrySelected');
$routes->get('detail/(:num)', 'WorkerLogController::detail/$1');
});

View File

@@ -6,5 +6,5 @@ use CodeIgniter\Router\RouteCollection;
/** @var RouteCollection $routes */ /** @var RouteCollection $routes */
$routes->group('kiso', function(RouteCollection $routes) { $routes->group('kiso', function(RouteCollection $routes) {
$routes->match(['get', 'post'], 'api/vrfcReq', 'KisoController::vrfcReq'); $routes->match(['GET', 'POST'], 'api/vrfcReq', 'KisoController::vrfcReq');
}); });

View File

@@ -4,7 +4,7 @@ namespace Config;
use CodeIgniter\Config\BaseConfig; use CodeIgniter\Config\BaseConfig;
use CodeIgniter\Session\Handlers\BaseHandler; use CodeIgniter\Session\Handlers\BaseHandler;
use CodeIgniter\Session\Handlers\FileHandler; use CodeIgniter\Session\Handlers\DatabaseHandler;
use CodeIgniter\Session\Handlers\RedisHandler; use CodeIgniter\Session\Handlers\RedisHandler;
class Session extends BaseConfig class Session extends BaseConfig
@@ -15,14 +15,11 @@ class Session extends BaseConfig
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
* *
* The session storage driver to use: * The session storage driver to use:
* - `CodeIgniter\Session\Handlers\FileHandler`
* - `CodeIgniter\Session\Handlers\DatabaseHandler` * - `CodeIgniter\Session\Handlers\DatabaseHandler`
* - `CodeIgniter\Session\Handlers\MemcachedHandler`
* - `CodeIgniter\Session\Handlers\RedisHandler` * - `CodeIgniter\Session\Handlers\RedisHandler`
* *
* @var class-string<BaseHandler> * @var class-string<BaseHandler>
*/ */
// public string $driver = FileHandler::class;
public string $driver = RedisHandler::class; public string $driver = RedisHandler::class;
/** /**
@@ -51,16 +48,61 @@ class Session extends BaseConfig
* *
* The location to save sessions to and is driver dependent. * The location to save sessions to and is driver dependent.
* *
* For the 'files' driver, it's a path to a writable directory.
* WARNING: Only absolute paths are supported!
*
* For the 'database' driver, it's a table name. * For the 'database' driver, it's a table name.
* Please read up the manual for the format with other session drivers. * For Redis: tcp://host:port?database=n&password=xxx
* *
* IMPORTANT: You are REQUIRED to set a valid save path! * IMPORTANT: You are REQUIRED to set a valid save path!
*/ */
// public string $savePath = WRITEPATH . 'session'; public string $savePath;
public string $savePath = 'tcp://192.168.10.243:6379?database=0';
public function __construct()
{
parent::__construct();
// 환경변수로 강제로 Database 모드 설정 가능 (Redis 장애 시 수동 전환)
$forceDatabase = env('SESSION_FORCE_DATABASE', false);
// Redis 설정: Redis 연결 실패 시 Database로 폴백
if ($this->driver === RedisHandler::class && !$forceDatabase) {
helper('redis');
try {
// Redis 연결 테스트
$testRedis = get_redis_connection('session');
if (!$testRedis) {
throw new \Exception('Redis connection failed');
}
// Redis 정상 - Redis 설정 사용
$config = get_redis_config('session');
$this->savePath = sprintf(
'tcp://%s:%s?database=%s',
$config['host'],
$config['port'],
$config['database']
);
if (!empty($config['password'])) {
$this->savePath .= '&password=' . $config['password'];
}
} catch (\Exception $e) {
// Redis 실패 - DatabaseHandler로 폴백
log_message('warning', 'Session: Redis unavailable (' . $e->getMessage() . '), falling back to DatabaseHandler');
$this->driver = DatabaseHandler::class;
$this->savePath = 'ci_sessions'; // 테이블 이름
}
} else {
// Database 강제 모드 또는 기본 Database 설정
if ($forceDatabase && $this->driver === RedisHandler::class) {
log_message('info', 'Session: Forced to use DatabaseHandler (SESSION_FORCE_DATABASE=true)');
$this->driver = DatabaseHandler::class;
}
$this->savePath = 'ci_sessions';
}
}
/** /**
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------

View File

@@ -109,42 +109,100 @@ class Processible extends BaseController
// 지역별 수량 저장 // 지역별 수량 저장
public function saveArea() public function saveArea()
{ {
try { log_message('info', '[Processible::saveArea] 진입');
try {
$rows = $this->request->getPost('rows'); $rows = $this->request->getPost('rows');
$rows = json_decode($rows, true); $rows = json_decode($rows, true);
// dd($rows); if (empty($rows)) {
// exit; log_message('info', '[Processible::saveArea] 저장가능한 데이터가 없습니다.');
if (count($rows) > 0) {
foreach ($rows as $row):
$this->model->saveArea($row);
endforeach;
return $this->response->setJSON([
'code' => '0',
'msg' => 'success'
]);
} else {
return $this->response->setJSON([ return $this->response->setJSON([
'code' => '9', 'code' => '9',
'msg' => '저장가능한 데이터가 없습니다.' 'msg' => '저장가능한 데이터가 없습니다.'
]); ]);
} }
// API 형식으로 변환 및 DB 저장
$syncSlotData = [];
foreach ($rows as $row) {
// DB 저장 (API 실패와 무관하게 저장됨)
$this->model->saveArea($row);
// API 데이터 구성
$syncSlotData[] = [
'baseDate' => $row['sc_date'],
'legalDivisionNumber' => $row['region_cd'],
'slots' => [
'am' => [
'max' => (int) $row['am_cnt'],
'reserved' => 0
],
'pm' => [
'max' => (int) $row['pm_cnt'],
'reserved' => 0
]
]
];
}
log_message('info', '[Processible::saveArea] DB 저장 완료 | Count: ' . count($rows));
// 네이버 API 슬롯 동기화 시도
log_message('info', '[Processible::saveArea] 슬롯 동기화 시작 | Count: ' . count($syncSlotData));
$naverClient = new \App\Libraries\NaverApiClient();
$apiResponse = $naverClient->syncSiteSlot($syncSlotData);
// API 응답 에러 체크
$hasError = false;
$errorMessage = '';
if ($apiResponse === null) {
$hasError = true;
$errorMessage = 'API 응답 없음 (null)';
} elseif (!empty($apiResponse['error'])) {
$hasError = true;
$errorType = $apiResponse['error_type'] ?? 'UNKNOWN';
$httpCode = $apiResponse['http_code'] ?? 'N/A';
$errorMessage = "API Error: {$errorType} (HTTP {$httpCode})";
} elseif (isset($apiResponse['http_code']) && $apiResponse['http_code'] >= 400) {
$hasError = true;
$errorMessage = "HTTP Error: {$apiResponse['http_code']}";
}
// API 에러 발생 시 (DB는 이미 저장됨)
if ($hasError) {
log_message('error', "[Processible::saveArea] {$errorMessage} | Response: " . json_encode($apiResponse, JSON_UNESCAPED_UNICODE));
return $this->response->setJSON([
'code' => '1',
'msg' => 'DB 저장은 완료되었으나 네이버 슬롯 동기화에 실패했습니다. 관리자에게 문의하세요.',
'error' => $errorMessage,
'dbSaved' => true,
'syncData' => $syncSlotData,
'apiResponse' => $apiResponse
]);
}
// 성공
log_message('info', '[Processible::saveArea] Naver API 슬롯 동기화 성공 | Response: ' . json_encode($apiResponse, JSON_UNESCAPED_UNICODE));
return $this->response->setJSON([
'code' => '0',
'msg' => 'success',
'dbSaved' => true,
'syncData' => $syncSlotData,
'apiResponse' => $apiResponse
]);
} catch (\Exception $e) { } catch (\Exception $e) {
log_message('error', '[Processible::saveArea] 예외 발생: ' . $e->getMessage());
return $this->response->setJSON([ return $this->response->setJSON([
'code' => '9', 'code' => '9',
'msg' => $e->getMessage(), 'msg' => '처리 중 오류가 발생했습니다: ' . $e->getMessage(),
]); ]);
} }
} }
// 기본 수량 저장
public function saveCount() public function saveCount()
{ {
try { try {
@@ -152,18 +210,24 @@ class Processible extends BaseController
$rows = $this->request->getPost('rows'); $rows = $this->request->getPost('rows');
$rows = json_decode($rows, true); $rows = json_decode($rows, true);
// dd($rows);
// exit;
if (count($rows) > 0) { if (count($rows) > 0) {
$results = [];
$hasError = false;
foreach ($rows as $row): foreach ($rows as $row):
$this->model->saveCount($row); $result = $this->model->saveCount($row);
$results[] = $result;
if (!$result['success']) {
$hasError = true;
}
endforeach; endforeach;
return $this->response->setJSON([ return $this->response->setJSON([
'code' => '0', 'code' => $hasError ? '9' : '0',
'msg' => 'success' 'msg' => $hasError ? '일부 저장 실패' : 'success',
'debug' => $results // 디버깅 정보 포함
]); ]);
} else { } else {

File diff suppressed because it is too large Load Diff

View File

@@ -4,16 +4,19 @@ namespace App\Controllers\Common;
use App\Controllers\BaseController; use App\Controllers\BaseController;
use App\Models\common\CommonModel; use App\Models\common\CommonModel;
use App\Models\manage\UserModel; use App\Models\manage\UserModel;
use App\Libraries\NaverApiClient;
class Common extends BaseController class Common extends BaseController
{ {
private $model; private $model;
private $userModel; private $userModel;
private $naverApiClient;
public function __construct() public function __construct()
{ {
$this->model = new CommonModel(); $this->model = new CommonModel();
$this->userModel = new UserModel(); $this->userModel = new UserModel();
$this->naverApiClient = new NaverApiClient();
} }
public function getVrfcCode() public function getVrfcCode()
@@ -118,4 +121,54 @@ class Common extends BaseController
]); ]);
} }
} }
// 단지 조회
public function getComplexList()
{
$legalDivisionNumber = $this->request->getGet("legalDivisionNumber");
$realEstateType = $this->request->getGet("realEstateType") ?? null;
try {
$complexList = $this->naverApiClient->getComplexList($legalDivisionNumber , $realEstateType);
return $this->response->setJSON($complexList);
} catch (\Exception $e) {
return $this->response->setJSON([
'code' => '9',
'msg' => $e->getMessage(),
]);
}
}
/**
* 평형 조회
*
* complexNumber : 단지 번호
* realEstateType : 매물 종류 (APT, OFFICETEL, VILLA 등)
*/
public function getPyeongInfo()
{
$complexNumber = $this->request->getGet("complexNumber");
$realEstateType = $this->request->getGet("realEstateType");
try {
if ( $realEstateType == 'A01' || $realEstateType == 'A02' || $realEstateType == 'A03' ) {
$pyeongInfo = $this->naverApiClient->getPyeongTypeList($complexNumber);
} else if ( $realEstateType == 'A05' || $realEstateType == 'A06' ) {
$pyeongInfo = $this->naverApiClient->getVillaPyeongTypeList($complexNumber);
} else {
return $this->response->setJSON([
'code' => '9',
'msg' => '지원하지 않는 매물 종류입니다.',
]);
}
return $this->response->setJSON($pyeongInfo);
} catch (\Exception $e) {
return $this->response->setJSON([
'code' => '9',
'msg' => $e->getMessage(),
]);
}
}
} }

View File

@@ -13,6 +13,23 @@ class KisoController extends BaseController
/** @var ResponseInterface */ /** @var ResponseInterface */
protected $response; protected $response;
/**
* 네이버 검증 요청 API
*
* Required Parameters:
* - articleNumber: 기사 번호
* - requestType: 요청 타입 (verify, check, validate)
* - requestDatetime: 요청 일시 (YYYY-MM-DD HH:MM:SS)
*
* Error Codes:
* - E001: 필수 파라미터 누락
* - E002: requestType 값 오류
* - E003: requestDatetime 형식 오류
* - E005: HTTP 메소드 오류
* - E999: Redis 연결 오류
*
* @return ResponseInterface
*/
public function vrfcReq() public function vrfcReq()
{ {
// 1. 요청 방식에 따라 데이터 파싱 // 1. 요청 방식에 따라 데이터 파싱
@@ -25,56 +42,79 @@ class KisoController extends BaseController
} else { } else {
// 지원하지 않는 메소드 처리 (예: PUT, DELETE 등) // 지원하지 않는 메소드 처리 (예: PUT, DELETE 등)
return $this->response->setStatusCode(Response::HTTP_METHOD_NOT_ALLOWED) return $this->response->setStatusCode(Response::HTTP_METHOD_NOT_ALLOWED)
->setJSON(['resultCode' => 'E005', 'resultMessage' => 'Method not allowed. Use GET or POST.']); ->setJSON(['code' => 'E005', 'message' => 'Method not allowed. Use GET or POST.']);
} }
// 2. 필수 항목 검증 // 2. 필수 항목 검증
$requiredKeys = ['articleNumber', 'requestType', 'requestDatetime']; $requiredKeys = ['articleNumber', 'requestType', 'requestDatetime'];
foreach ($requiredKeys as $key) { foreach ($requiredKeys as $key) {
// 파싱된 데이터($data) 내에 키가 없거나 값이 비어있는지 확인 // isset()과 trim()을 사용하여 '0' 값도 허용
if (empty($data[$key])) { if (!isset($data[$key]) || trim((string)$data[$key]) === '') {
return $this->response->setStatusCode(Response::HTTP_BAD_REQUEST) return $this->response->setStatusCode(Response::HTTP_BAD_REQUEST)
->setJSON([ ->setJSON([
'resultCode' => 'E001', 'code' => 'E001',
'resultMessage' => "Missing required parameter: {$key}" 'message' => "Missing required parameter: {$key}"
]); ]);
} }
} }
// 3. Redis 연결 및 예외 처리 // 3. requestType 값 검증
// 3. Redis 연결 및 직접 푸시 $validRequestTypes = ['verify', 'check', 'validate']; // 허용되는 requestType 값
try { if (!in_array($data['requestType'], $validRequestTypes, true)) {
$redis = new \Redis(); return $this->response->setStatusCode(Response::HTTP_BAD_REQUEST)
// Docker 환경이므로 host를 'redis'로 설정 ->setJSON([
$success = $redis->connect('redis', 6379); 'code' => 'E002',
'message' => "Invalid requestType. Allowed values: " . implode(', ', $validRequestTypes)
]);
}
if (!$success) { // 4. requestDatetime 날짜 형식 검증 (Y-m-d H:i:s 형식)
$datetime = \DateTime::createFromFormat('Y-m-d H:i:s', $data['requestDatetime']);
if (!$datetime || $datetime->format('Y-m-d H:i:s') !== $data['requestDatetime']) {
return $this->response->setStatusCode(Response::HTTP_BAD_REQUEST)
->setJSON([
'code' => 'E003',
'message' => "Invalid requestDatetime format. Use: YYYY-MM-DD HH:MM:SS"
]);
}
// 5. Redis 연결 및 큐 저장
helper('redis');
try {
$redis = get_redis_connection('worker');
if (!$redis) {
throw new \Exception('Redis connection failed'); throw new \Exception('Redis connection failed');
} }
$redis->select(10); // 10번 DB 선택
// 데이터 준비 // 데이터 준비
$data['retry_count'] = 0; $data['retry_count'] = 0;
$data['received_at'] = date('Y-m-d H:i:s');
// 리스트에 데이터 삽입 (이 명령어가 실행되어야 monitor에 LPUSH가 뜹니다) // 리스트에 데이터 삽입
$redis->lPush('naver:queue', json_encode($data)); $pushResult = $redis->lPush('naver:queue', json_encode($data));
if (!$pushResult) {
throw new \Exception('Failed to push data to Redis queue');
}
// 성공 로그 기록
log_message('info', "Request queued successfully - Article: {$data['articleNumber']}, Type: {$data['requestType']}");
} catch (\Exception $e) { } catch (\Exception $e) {
log_message('error', 'Redis Push Error: ' . $e->getMessage()); log_message('error', 'Redis Push Error: ' . $e->getMessage());
return $this->response->setStatusCode(500)->setJSON([ return $this->response->setStatusCode(500)->setJSON([
'resultCode' => 'E999', 'code' => 'E999',
'resultMessage' => 'Redis Connection Error' 'message' => 'Redis Connection Error'
]); ]);
} }
// 4. 성공 응답 // 6. 성공 응답
return $this->response->setStatusCode(Response::HTTP_ACCEPTED) // 202 Accepted return $this->response->setStatusCode(Response::HTTP_OK) // 200 OK
->setJSON([ ->setJSON([
'resultCode' => 'S000', 'code' => 'success',
'resultMessage' => 'Request successfully queued for processing', 'message' => ''
'articleNumber' => $data['articleNumber']
]); ]);
} }
} }

View File

@@ -0,0 +1,27 @@
<?php
namespace App\Controllers;
class LogTest extends BaseController
{
public function index()
{
log_message('error', '===== WEB LOG TEST ===== ' . date('Y-m-d H:i:s'));
log_message('info', 'Info from web');
log_message('debug', 'Debug from web');
$logFile = WRITEPATH . 'logs/log-' . date('Y-m-d') . '.log';
echo "ENVIRONMENT: " . ENVIRONMENT . "<br>";
echo "Log file: " . $logFile . "<br>";
echo "Exists: " . (file_exists($logFile) ? 'YES' : 'NO') . "<br>";
echo "Writable: " . (is_writable(dirname($logFile)) ? 'YES' : 'NO') . "<br>";
if (file_exists($logFile)) {
echo "<h3>Last 20 lines:</h3>";
echo "<pre>";
echo htmlspecialchars(shell_exec('tail -20 ' . escapeshellarg($logFile)));
echo "</pre>";
}
}
}

View File

@@ -3,6 +3,7 @@ namespace App\Controllers;
use App\Controllers\BaseController; use App\Controllers\BaseController;
use App\Models\common\LoginModel; use App\Models\common\LoginModel;
use CodeIgniter\Session\Handlers\DatabaseHandler;
class Login extends BaseController class Login extends BaseController
{ {
@@ -13,6 +14,37 @@ class Login extends BaseController
$this->loginModel = new LoginModel(); $this->loginModel = new LoginModel();
} }
/**
* Redis 폴백 상태 체크 (세션이 DatabaseHandler로 동작 중인지)
*/
private function getSystemStatus(): array
{
$status = [
'redis_fallback' => false,
'warning_message' => ''
];
// Session 설정에서 현재 드라이버 확인
$sessionConfig = config('Session');
if ($sessionConfig->driver === DatabaseHandler::class) {
$status['redis_fallback'] = true;
$status['warning_message'] = '세션 서버(Redis) 장애로 임시 모드로 운영 중입니다. 시스템 관리자에게 문의하세요.';
}
return $status;
}
/**
* JSON 응답에 시스템 상태 추가
*/
private function jsonResponse(array $data): \CodeIgniter\HTTP\ResponseInterface
{
$systemStatus = $this->getSystemStatus();
$data['system'] = $systemStatus;
return $this->response->setJSON($data);
}
public function index(): string public function index(): string
{ {
$user_id = get_cookie('save_id'); $user_id = get_cookie('save_id');
@@ -59,7 +91,7 @@ class Login extends BaseController
]; ];
if (!$this->validate($rules)) { if (!$this->validate($rules)) {
return $this->response->setJSON([ return $this->jsonResponse([
'code' => '1', 'code' => '1',
'errors' => $this->validator->getErrors() 'errors' => $this->validator->getErrors()
]); ]);
@@ -82,7 +114,7 @@ class Login extends BaseController
$this->loginModel->insertUserLog($logs); $this->loginModel->insertUserLog($logs);
return $this->response->setJSON([ return $this->jsonResponse([
'code' => '1', 'code' => '1',
'msg' => '존재하지 않는 아이디입니다.' 'msg' => '존재하지 않는 아이디입니다.'
]); ]);
@@ -96,7 +128,7 @@ class Login extends BaseController
$this->loginModel->insertUserLog($logs); $this->loginModel->insertUserLog($logs);
return $this->response->setJSON(body: [ return $this->jsonResponse([
'code' => '1', 'code' => '1',
'msg' => '잘못된 비밀번호 입니다.' 'msg' => '잘못된 비밀번호 입니다.'
]); ]);
@@ -135,7 +167,7 @@ class Login extends BaseController
$this->session->set($newdata); $this->session->set($newdata);
return $this->response->setJSON([ return $this->jsonResponse([
'code' => '0', 'code' => '0',
'msg' => 'success' 'msg' => 'success'
]); ]);
@@ -148,7 +180,17 @@ class Login extends BaseController
log_message('error', '[LOGIN ERROR] ' . $e->getMessage()); log_message('error', '[LOGIN ERROR] ' . $e->getMessage());
log_message('error', $e->getTraceAsString()); log_message('error', $e->getTraceAsString());
return $this->response->setJSON([ // 세션 관련 에러인지 확인
$errorMessage = $e->getMessage();
if (stripos($errorMessage, 'session') !== false ||
stripos($errorMessage, 'redis') !== false) {
return $this->jsonResponse([
'code' => '8',
'msg' => '세션 서비스 오류입니다. 시스템 관리자에게 문의해주세요.'
]);
}
return $this->jsonResponse([
'code' => '9', 'code' => '9',
'msg' => '서버 내부 오류가 발생했습니다. 잠시 후 다시 시도해주세요.' 'msg' => '서버 내부 오류가 발생했습니다. 잠시 후 다시 시도해주세요.'
]); ]);

View File

@@ -0,0 +1,222 @@
<?php
namespace App\Controllers\Manage;
use App\Controllers\BaseController;
class WorkerLog extends BaseController
{
/**
* Worker 로그 통합 뷰어
*/
public function index()
{
$this->data['title'] = 'Worker 로그 통합 관리';
// 로그 디렉토리 목록
$logDirs = [
'api_receiver' => ROOTPATH . 'worker/logs',
'naver_worker' => WRITEPATH . 'logs/worker'
];
// 날짜 필터 (기본값: 오늘)
$date = $this->request->getGet('date') ?? date('Y-m-d');
$logType = $this->request->getGet('type') ?? 'all';
$logs = [];
// API Receiver 로그 읽기
if ($logType === 'all' || $logType === 'api_receiver') {
$apiLogFile = $logDirs['api_receiver'] . '/' . $date . '.log';
if (file_exists($apiLogFile)) {
$logs['api_receiver'] = $this->parseLogFile($apiLogFile);
}
}
// Naver Worker 로그 읽기
if ($logType === 'all' || $logType === 'naver_worker') {
$workerLogFile = $logDirs['naver_worker'] . '/' . $date . '.log';
if (file_exists($workerLogFile)) {
$logs['naver_worker'] = $this->parseLogFile($workerLogFile);
}
// Failed 로그도 읽기
$failedLogFile = $logDirs['naver_worker'] . '/' . $date . '_failed.log';
if (file_exists($failedLogFile)) {
$logs['naver_worker_failed'] = $this->parseLogFile($failedLogFile);
}
}
// 모든 로그를 시간순으로 통합 정렬
$allLogs = [];
foreach ($logs as $type => $entries) {
foreach ($entries as $entry) {
$entry['source'] = $type;
$allLogs[] = $entry;
}
}
// 시간 역순 정렬 (최신순)
usort($allLogs, function($a, $b) {
return strtotime($b['timestamp']) - strtotime($a['timestamp']);
});
$this->data['logs'] = $allLogs;
$this->data['date'] = $date;
$this->data['logType'] = $logType;
$this->data['logDirs'] = $logDirs;
// 사용 가능한 날짜 목록 (최근 30일)
$this->data['availableDates'] = $this->getAvailableLogDates($logDirs);
return view('pages/manage/worker_log', $this->data);
}
/**
* 로그 파일 파싱
*/
private function parseLogFile($filePath)
{
$logs = [];
$lines = file($filePath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
foreach ($lines as $line) {
$logs[] = $this->parseLogLine($line);
}
return $logs;
}
/**
* 로그 한 줄 파싱
*/
private function parseLogLine($line)
{
// 로그 포맷: [2025-12-22 10:30:45] [INFO] [NaverWorker::run:95] 메시지
if (preg_match('/\[(.+?)\]\s*\[(.+?)\]\s*(?:\[(.+?)\]\s*)?(.+)/', $line, $matches)) {
return [
'timestamp' => $matches[1],
'level' => $matches[2],
'location' => $matches[3] ?? '',
'message' => $matches[4]
];
}
// 파싱 실패한 경우 원본 그대로
return [
'timestamp' => date('Y-m-d H:i:s'),
'level' => 'UNKNOWN',
'location' => '',
'message' => $line
];
}
/**
* 로그 파일이 존재하는 날짜 목록 가져오기
*/
private function getAvailableLogDates($logDirs)
{
$dates = [];
foreach ($logDirs as $dir) {
if (!is_dir($dir)) continue;
$files = scandir($dir);
foreach ($files as $file) {
if (preg_match('/(\d{4}-\d{2}-\d{2})(?:_failed)?\.log$/', $file, $matches)) {
$dates[$matches[1]] = true;
}
}
}
$dates = array_keys($dates);
rsort($dates); // 최신순 정렬
return array_slice($dates, 0, 30); // 최근 30일만
}
/**
* 실시간 로그 스트리밍 (Ajax)
*/
public function stream()
{
$type = $this->request->getGet('type') ?? 'naver_worker';
$lastId = (int) ($this->request->getGet('lastId') ?? 0);
$logDirs = [
'api_receiver' => ROOTPATH . 'worker/logs',
'naver_worker' => WRITEPATH . 'logs/worker'
];
$logFile = $logDirs[$type] . '/' . date('Y-m-d') . '.log';
$newLogs = [];
if (file_exists($logFile)) {
$lines = file($logFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
// lastId 이후의 로그만 반환
if ($lastId < count($lines)) {
$newLines = array_slice($lines, $lastId);
foreach ($newLines as $line) {
$newLogs[] = $this->parseLogLine($line);
}
}
}
return $this->response->setJSON([
'success' => true,
'logs' => $newLogs,
'lastId' => $lastId + count($newLogs)
]);
}
/**
* 로그 파일 다운로드
*/
public function download()
{
$date = $this->request->getGet('date') ?? date('Y-m-d');
$type = $this->request->getGet('type') ?? 'naver_worker';
$logDirs = [
'api_receiver' => ROOTPATH . 'worker/logs',
'naver_worker' => WRITEPATH . 'logs/worker'
];
$logFile = $logDirs[$type] . '/' . $date . '.log';
if (!file_exists($logFile)) {
return $this->response->setStatusCode(404)->setBody('로그 파일을 찾을 수 없습니다.');
}
return $this->response->download($logFile, null);
}
/**
* 로그 파일 삭제
*/
public function delete()
{
$date = $this->request->getPost('date');
$type = $this->request->getPost('type');
if (!$date || !$type) {
return $this->response->setJSON(['success' => false, 'message' => '필수 파라미터 누락']);
}
$logDirs = [
'api_receiver' => ROOTPATH . 'worker/logs',
'naver_worker' => WRITEPATH . 'logs/worker'
];
$logFile = $logDirs[$type] . '/' . $date . '.log';
if (file_exists($logFile)) {
unlink($logFile);
return $this->response->setJSON(['success' => true, 'message' => '로그 파일이 삭제되었습니다.']);
}
return $this->response->setJSON(['success' => false, 'message' => '로그 파일을 찾을 수 없습니다.']);
}
}

View File

@@ -0,0 +1,366 @@
<?php
namespace App\Controllers\Manage;
use App\Controllers\BaseController;
use App\Models\Entities\NaverWorkerLogModel;
class WorkerLogController extends BaseController
{
protected $logModel;
public function __construct()
{
$this->logModel = new NaverWorkerLogModel();
}
/**
* 실패 로그 목록 (오류 유형별 분석)
*/
public function failedList()
{
$db = \Config\Database::connect();
// 1. 전체 통계
$stats = [
'total_fail' => $this->logModel->where('status', 'FAIL')->countAllResults(false),
'retry_available' => $this->logModel
->where('status', 'FAIL')
->groupStart()
->where('retry_cnt IS NULL')
->orWhere('retry_cnt <', 3)
->groupEnd()
->countAllResults(false),
'retry_exhausted' => $this->logModel
->where('status', 'FAIL')
->where('retry_cnt >=', 3)
->countAllResults(false),
];
// 2. 오류 유형별 분류
$errorTypes = $this->classifyErrors();
// 3. 최근 실패 목록
$recentFails = $this->logModel
->select('seq, atcl_no, status, error_msg, retry_cnt, created_at')
->where('status', 'FAIL')
->orderBy('created_at', 'DESC')
->findAll(50);
// 각 오류에 대한 해결 가이드 추가
foreach ($recentFails as &$log) {
$log['error_type'] = $this->detectErrorType($log['error_msg']);
$log['solution'] = $this->getSolution($log['error_type']);
$log['can_retry'] = $this->canRetry($log);
}
$data = [
'stats' => $stats,
'errorTypes' => $errorTypes,
'logs' => $recentFails
];
return view('manage/worker_log/failed_list', array_merge($this->data, $data));
}
/**
* 오류 유형 분류
*/
protected function classifyErrors()
{
$db = \Config\Database::connect();
$errorPatterns = [
'foreign_key' => [
'pattern' => 'foreign key constraint',
'label' => 'FK 제약조건 위반',
'severity' => 'high',
'count' => 0
],
'duplicate' => [
'pattern' => 'Duplicate entry',
'label' => '중복 데이터',
'severity' => 'medium',
'count' => 0
],
'missing_user' => [
'pattern' => 'usr_sq.*users 테이블에 없음',
'label' => '담당자 정보 없음',
'severity' => 'medium',
'count' => 0
],
'empty_payload' => [
'pattern' => '빈 페이로드',
'label' => '빈 데이터',
'severity' => 'low',
'count' => 0
],
'api_error' => [
'pattern' => 'API.*FAIL|HTTP.*40[0-9]|HTTP.*50[0-9]',
'label' => 'API 통신 오류',
'severity' => 'medium',
'count' => 0
],
'unknown' => [
'pattern' => '',
'label' => '기타 오류',
'severity' => 'low',
'count' => 0
]
];
$failedLogs = $this->logModel
->select('error_msg')
->where('status', 'FAIL')
->findAll();
foreach ($failedLogs as $log) {
$classified = false;
foreach ($errorPatterns as $key => &$pattern) {
if ($key === 'unknown') continue;
if ($pattern['pattern'] && preg_match('/' . $pattern['pattern'] . '/i', $log['error_msg'])) {
$pattern['count']++;
$classified = true;
break;
}
}
if (!$classified) {
$errorPatterns['unknown']['count']++;
}
}
return $errorPatterns;
}
/**
* 오류 유형 감지
*/
protected function detectErrorType($errorMsg)
{
if (!$errorMsg) return 'unknown';
if (stripos($errorMsg, 'foreign key constraint') !== false) {
return 'foreign_key';
}
if (stripos($errorMsg, 'Duplicate entry') !== false) {
return 'duplicate';
}
if (stripos($errorMsg, 'usr_sq') !== false && stripos($errorMsg, 'users 테이블') !== false) {
return 'missing_user';
}
if (stripos($errorMsg, '빈 페이로드') !== false) {
return 'empty_payload';
}
if (preg_match('/API.*FAIL|HTTP.*40[0-9]|HTTP.*50[0-9]/i', $errorMsg)) {
return 'api_error';
}
return 'unknown';
}
/**
* 오류 유형별 해결 방법
*/
protected function getSolution($errorType)
{
$solutions = [
'foreign_key' => [
'title' => 'DB 데이터 수정 필요',
'steps' => [
'1. 참조하는 테이블의 데이터가 존재하는지 확인',
'2. region 테이블의 orphaned usr_sq 값 수정',
'3. TypeSParameterMapper의 폴백 로직 확인'
],
'auto_retry' => false
],
'duplicate' => [
'title' => '중복 데이터 처리',
'steps' => [
'1. 기존 데이터 확인 (atcl_no로 검색)',
'2. 중복 원인 파악 (동일 요청 2회 수신?)',
'3. 필요시 기존 데이터 삭제 후 재처리'
],
'auto_retry' => false
],
'missing_user' => [
'title' => '담당자 정보 수정됨 - 재처리 가능',
'steps' => [
'✅ TypeSParameterMapper가 자동 폴백 적용됨',
'✅ 바로 재처리 가능 (usr_sq=1로 처리됨)'
],
'auto_retry' => true
],
'empty_payload' => [
'title' => '잘못된 요청 데이터',
'steps' => [
'1. raw_payload 확인',
'2. 네이버 API 응답 확인',
'3. 재처리 불가 - 데이터 삭제 권장'
],
'auto_retry' => false
],
'api_error' => [
'title' => 'API 통신 오류',
'steps' => [
'1. 일시적 오류인지 확인 (네트워크, 타임아웃)',
'2. 네이버 API 서버 상태 확인',
'3. 일시적 오류면 재처리 가능'
],
'auto_retry' => true
],
'unknown' => [
'title' => '원인 미상',
'steps' => [
'1. error_msg 상세 확인',
'2. 로그 파일 확인',
'3. 개발팀 검토 필요'
],
'auto_retry' => false
]
];
return $solutions[$errorType] ?? $solutions['unknown'];
}
/**
* 재시도 가능 여부
*/
protected function canRetry($log)
{
// 재시도 횟수 체크
if (($log['retry_cnt'] ?? 0) >= 3) {
return false;
}
// 오류 유형별 재시도 가능 여부
$errorType = $this->detectErrorType($log['error_msg']);
$solution = $this->getSolution($errorType);
return $solution['auto_retry'] ?? false;
}
/**
* 선택한 로그 재처리 (AJAX)
*/
public function retrySelected()
{
$logIds = $this->request->getPost('log_ids');
if (empty($logIds) || !is_array($logIds)) {
return $this->response->setJSON([
'success' => false,
'message' => '재처리할 로그를 선택해주세요.'
]);
}
$naverService = new \App\Services\NaverService();
$results = [
'success' => 0,
'fail' => 0,
'details' => []
];
foreach ($logIds as $logId) {
$log = $this->logModel->find($logId);
if (!$log) {
$results['details'][] = [
'id' => $logId,
'status' => 'error',
'message' => '로그를 찾을 수 없습니다.'
];
continue;
}
// 재시도 가능 여부 체크
if (!$this->canRetry($log)) {
$results['details'][] = [
'id' => $logId,
'atcl_no' => $log['atcl_no'],
'status' => 'skip',
'message' => '재시도 불가능 (횟수 초과 또는 수정 필요)'
];
continue;
}
try {
// 재처리
$this->logModel->update($logId, ['status' => 'RETRY']);
$responseJson = json_decode($log['raw_payload'], true);
$payload = $responseJson['request_data'] ?? [];
if (empty($payload)) {
throw new \Exception("빈 페이로드 데이터");
}
$insertId = $naverService->processArticle($payload);
$this->logModel->update($logId, [
'atcl_no' => $payload['articleNumber'] ?? null,
'status' => 'SUCCESS',
'target_db_id' => $insertId,
'error_msg' => null,
'retry_cnt' => ($log['retry_cnt'] ?? 0) + 1
]);
$results['success']++;
$results['details'][] = [
'id' => $logId,
'atcl_no' => $log['atcl_no'],
'status' => 'success',
'message' => "성공 (DB ID: {$insertId})"
];
} catch (\Exception $e) {
$this->logModel->update($logId, [
'status' => 'FAIL',
'error_msg' => $e->getMessage(),
'retry_cnt' => ($log['retry_cnt'] ?? 0) + 1
]);
$results['fail']++;
$results['details'][] = [
'id' => $logId,
'atcl_no' => $log['atcl_no'],
'status' => 'fail',
'message' => $e->getMessage()
];
}
}
return $this->response->setJSON([
'success' => true,
'results' => $results
]);
}
/**
* 특정 로그 상세 (AJAX)
*/
public function detail($id)
{
$log = $this->logModel->find($id);
if (!$log) {
return $this->response->setJSON([
'success' => false,
'message' => '로그를 찾을 수 없습니다.'
]);
}
// raw_payload JSON 파싱
$log['parsed_payload'] = json_decode($log['raw_payload'], true);
$log['error_type'] = $this->detectErrorType($log['error_msg']);
$log['solution'] = $this->getSolution($log['error_type']);
$log['can_retry'] = $this->canRetry($log);
return $this->response->setJSON([
'success' => true,
'log' => $log
]);
}
}

View File

@@ -31,6 +31,7 @@ class Assign extends BaseController
{ {
$start = (int) $this->request->getGet('start') ?: 0; $start = (int) $this->request->getGet('start') ?: 0;
$end = (int) $this->request->getGet('length') ?: 10; $end = (int) $this->request->getGet('length') ?: 10;
$draw = (int) $this->request->getGet('draw') ?: 1;
$data = [ $data = [
'bonbu' => $this->request->getGet('bonbu'), 'bonbu' => $this->request->getGet('bonbu'),
@@ -42,12 +43,18 @@ class Assign extends BaseController
'srchTxt' => $this->request->getGet('srchTxt'), 'srchTxt' => $this->request->getGet('srchTxt'),
]; ];
$totalCount = $this->assignModel->getTotalCount($data);
$datas = $this->assignModel->getUserList($start, $end, $data); $datas = $this->assignModel->getUserList($start, $end, $data);
// 첫 번째 행에서 total_count 추출 (없으면 0)
$totalCount = !empty($datas) ? (int)$datas[0]['total_count'] : 0;
// 각 행에서 total_count 컬럼 제거
foreach ($datas as &$row) {
unset($row['total_count']);
}
return $this->response->setJSON(body: [ return $this->response->setJSON(body: [
'draw' => $draw,
'recordsTotal' => $totalCount, 'recordsTotal' => $totalCount,
'recordsFiltered' => $totalCount, 'recordsFiltered' => $totalCount,
'data' => $datas, 'data' => $datas,

View File

@@ -0,0 +1,163 @@
<?php
namespace App\Controllers\V2;
use App\Controllers\BaseController;
use App\Models\common\CodeModel;
/**
* BaseV2Controller
* V2 모듈 공통 로직: lists / getResultList / excel
*
* 하위 클래스에서 구현해야 하는 메서드:
* - createModel() : 모듈 전용 Model 인스턴스 반환
* - getCodeKeys() : lists()에서 로드할 코드 카테고리 배열 반환
* - getViewName() : lists()에서 렌더링할 뷰 경로 반환
* - getSearchKeys() : getResultList/excel에서 수집할 GET 파라미터 키 배열 반환
*/
abstract class BaseV2Controller extends BaseController
{
protected $model;
protected $codeModel;
public function __construct()
{
$this->codeModel = new CodeModel();
$this->model = $this->createModel();
}
abstract protected function createModel();
abstract protected function getCodeKeys(): array;
abstract protected function getViewName(): string;
abstract protected function getSearchKeys(): array;
public function lists(): string
{
$codes = $this->codeModel->getCodeLists($this->getCodeKeys());
$sido = $this->model->getAreaList();
$bonbu = $this->model->getBonbuList();
$team = $this->model->getTeamList();
$user = $this->model->getUserList();
$this->data['codes'] = $codes;
$this->data['sido'] = $sido;
$this->data['bonbu'] = $bonbu;
$this->data['team'] = $team;
$this->data['user'] = $user;
return view($this->getViewName(), $this->data);
}
public function getResultList()
{
$start = (int) $this->request->getGet('start') ?: 0;
$end = (int) $this->request->getGet('length') ?: 10;
$data = $this->buildSearchData();
$totalCount = $this->model->getTotalCount($data);
$datas = $this->model->getResultList($start, $end, $data);
return $this->response->setJSON([
'recordsTotal' => $totalCount,
'recordsFiltered' => $totalCount,
'data' => $datas,
]);
}
public function excel()
{
try {
$data = $this->buildSearchData();
$datas = $this->model->getExcelList($data);
return $this->response->setJSON(['data' => $datas]);
} catch (\Exception $e) {
$e->getPrevious()->getTraceAsString();
}
}
/**
* getSearchKeys()에 정의된 키만 GET에서 수집해서 배열로 반환
*/
protected function buildSearchData(): array
{
$data = [];
foreach ($this->getSearchKeys() as $key) {
$rawValue = $this->request->getGet($key);
$data[$key] = $this->normalizeSearchValue($key, $rawValue);
}
return $data;
}
/**
* 공통 파라미터 정규화
* - null/공백은 빈 문자열로 통일
* - 문자열은 trim 처리
* - 날짜 키(date 포함)는 형식 검증 실패 시 빈 문자열로 처리
*/
protected function normalizeSearchValue(string $key, $value)
{
if (is_array($value)) {
return array_map(static function ($item) {
if ($item === null) {
return '';
}
if (is_string($item)) {
$item = trim($item);
return $item === '' ? '' : $item;
}
return $item;
}, $value);
}
if ($value === null) {
return '';
}
if (is_string($value)) {
$value = trim($value);
if ($value === '') {
return '';
}
}
if ($this->isDateKey($key) && !$this->isValidDateValue((string) $value)) {
return '';
}
return $value;
}
/**
* 날짜 성격 파라미터 판별
* 예: receipt_sdate, receipt_edate, complete_sdate, stat_complete_date 등
*/
protected function isDateKey(string $key): bool
{
return strpos($key, 'date') !== false;
}
/**
* 허용 날짜 형식 검증
* - YYYY-MM-DD
* - YYYYMMDD
*/
protected function isValidDateValue(string $value): bool
{
if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $value) === 1) {
[$year, $month, $day] = explode('-', $value);
return checkdate((int) $month, (int) $day, (int) $year);
}
if (preg_match('/^\d{8}$/', $value) === 1) {
$year = substr($value, 0, 4);
$month = substr($value, 4, 2);
$day = substr($value, 6, 2);
return checkdate((int) $month, (int) $day, (int) $year);
}
return false;
}
}

View File

@@ -1,130 +1,60 @@
<?php <?php
namespace App\Controllers\V2; namespace App\Controllers\V2;
use App\Controllers\BaseController; use App\Controllers\V2\BaseV2Controller;
use App\Libraries\FormValidation; use App\Libraries\FormValidation;
use App\Libraries\MyUpload; use App\Libraries\MyUpload;
use App\Libraries\NaverApiClient; use App\Libraries\NaverApiClient;
use App\Models\common\CodeModel;
use App\Models\results\M415Model; use App\Models\results\M415Model;
use App\Models\v2\M701Model;
use App\Models\v2\M710Model; use App\Models\v2\M710Model;
use Exception; use Exception;
use function PHPUnit\Framework\throwException; use App\Models\v2\M701Model;
class M701 extends BaseController class M701 extends BaseV2Controller
{ {
private $model, $codeModel; protected function createModel()
public function __construct()
{ {
$this->model = new M701Model(); return new M701Model();
$this->codeModel = new CodeModel();
} }
public function lists(): string protected function getCodeKeys(): array
{ {
return ['STEP_VERIFICATION', 'VRFCREQ_WAY', 'CP_ID', 'ARTICLE_TYPE'];
$codes = $this->codeModel->getCodeLists(['STEP_VERIFICATION', 'VRFCREQ_WAY', 'CP_ID', 'ARTICLE_TYPE']); // 코드조회
$sido = $this->model->getAreaList(); // 지역조회
$bonbu = $this->model->getBonbuList();
$team = $this->model->getTeamList();
$user = $this->model->getUserList();
$this->data['sido'] = $sido;
$this->data['bonbu'] = $bonbu;
$this->data['team'] = $team;
$this->data['user'] = $user;
$this->data['codes'] = $codes;
return view("pages/v2/m701/lists", $this->data);
} }
public function getResultList() protected function getViewName(): string
{ {
$start = (int) $this->request->getGet('start') ?: 0; return 'pages/v2/m701/lists';
$end = (int) $this->request->getGet('length') ?: 10; }
$data = [ protected function getSearchKeys(): array
'atcl_no' => $this->request->getGet('atcl_no'), // 매물번호 {
'stat_cd' => $this->request->getGet('stat_cd'), // 현재상태 return [
'realtor_nm' => $this->request->getGet('realtor_nm'), // 중개소 'atcl_no',
'charger_gbn' => $this->request->getGet('charger_gbn'), // 배정여부 'stat_cd',
'assign_yn' => $this->request->getGet('assign_yn'), // 배정여부2 'realtor_nm',
'receipt_sdate' => $this->request->getGet('receipt_sdate'), // 접수기간1 'charger_gbn',
'receipt_edate' => $this->request->getGet('receipt_edate'), // 접수기간2 'assign_yn',
'complete_sdate' => $this->request->getGet('complete_sdate'), // 완료기간1 'receipt_sdate',
'complete_edate' => $this->request->getGet('complete_edate'), // 완료기간2 'receipt_edate',
'srcSido' => $this->request->getGet('srcSido'), // 시도 'complete_sdate',
'srcGugun' => $this->request->getGet('srcGugun'), // 시군구 'complete_edate',
'srcDong' => $this->request->getGet('srcDong'), // 읍면동 'srcSido',
'bonbu' => $this->request->getGet('bonbu'), // 본부 'srcGugun',
'team' => $this->request->getGet('team'), // 팀 'srcDong',
'damdang' => $this->request->getGet('damdang'), // 담당 'bonbu',
'vrfcreq_way' => $this->request->getGet('vrfcreq_way'), // 검증방식1 'team',
'vrfc_type_sub' => $this->request->getGet('vrfc_type_sub'), // 검증방식2 'damdang',
'rcpt_cpid' => $this->request->getGet('rcpt_cpid'), // 매체사 'vrfcreq_way',
'rlet_type_cd' => $this->request->getGet('rlet_type_cd'), // 매물종류 'vrfc_type_sub',
'reference_file_url_yn' => $this->request->getGet('reference_file_url_yn'), // 참고용 'rcpt_cpid',
'corp_own' => $this->request->getGet('corp_own'), // 법인 'rlet_type_cd',
'reference_file_url_yn',
'corp_own',
]; ];
$totalCount = $this->model->getTotalCount($data);
$datas = $this->model->getResultList($start, $end, $data);
return $this->response->setJSON(body: [
'recordsTotal' => $totalCount,
'recordsFiltered' => $totalCount,
'data' => $datas,
]);
} }
// 엑셀 다운로드
public function excel()
{
try {
$data = [
'atcl_no' => $this->request->getGet('atcl_no'), // 매물번호
'stat_cd' => $this->request->getGet('stat_cd'), // 현재상태
'realtor_nm' => $this->request->getGet('realtor_nm'), // 중개소
'charger_gbn' => $this->request->getGet('charger_gbn'), // 배정여부
'assign_yn' => $this->request->getGet('assign_yn'), // 배정여부2
'receipt_sdate' => $this->request->getGet('receipt_sdate'), // 접수기간1
'receipt_edate' => $this->request->getGet('receipt_edate'), // 접수기간2
'complete_sdate' => $this->request->getGet('complete_sdate'), // 완료기간1
'complete_edate' => $this->request->getGet('complete_edate'), // 완료기간2
'srcSido' => $this->request->getGet('srcSido'), // 시도
'srcGugun' => $this->request->getGet('srcGugun'), // 시군구
'srcDong' => $this->request->getGet('srcDong'), // 읍면동
'bonbu' => $this->request->getGet('bonbu'), // 본부
'team' => $this->request->getGet('team'), // 팀
'damdang' => $this->request->getGet('damdang'), // 담당
'vrfcreq_way' => $this->request->getGet('vrfcreq_way'), // 검증방식1
'vrfc_type_sub' => $this->request->getGet('vrfc_type_sub'), // 검증방식2
'rcpt_cpid' => $this->request->getGet('rcpt_cpid'), // 매체사
'rlet_type_cd' => $this->request->getGet('rlet_type_cd'), // 매물종류
'reference_file_url_yn' => $this->request->getGet('reference_file_url_yn'), // 참고용
'corp_own' => $this->request->getGet('corp_own'), // 법인
];
$datas = $this->model->getExcelList($data);
return $this->response->setJSON(body: [
'data' => $datas,
]);
} catch (\Exception $e) {
$e->getPrevious()->getTraceAsString();
}
}
// 상세화면
public function detail($id = null) public function detail($id = null)
{ {
$id = (int) $id; $id = (int) $id;
@@ -1700,5 +1630,3 @@ class M701 extends BaseController
} }
} }
} }

View File

@@ -1,123 +1,58 @@
<?php <?php
namespace App\Controllers\V2; namespace App\Controllers\V2;
use App\Controllers\BaseController; use App\Controllers\V2\BaseV2Controller;
use App\Libraries\MyUpload; use App\Libraries\MyUpload;
use App\Libraries\NaverApiClient; use App\Libraries\NaverApiClient;
use App\Models\common\CodeModel;
use App\Models\results\M415Model; use App\Models\results\M415Model;
use App\Models\v2\M702Model;
use App\Models\v2\M710Model; use App\Models\v2\M710Model;
use Exception; use Exception;
use App\Models\v2\M702Model;
class M702 extends BaseController class M702 extends BaseV2Controller
{ {
private $model; protected function createModel()
private $codeModel;
public function __construct()
{ {
$this->model = new M702Model(); return new M702Model();
$this->codeModel = new CodeModel();
} }
public function lists() protected function getCodeKeys(): array
{ {
$codes = $this->codeModel->getCodeLists(['STEP_VERIFICATION', 'VRFCREQ_WAY', 'CP_ID', 'ARTICLE_TYPE']); // 코드조회 return ['STEP_VERIFICATION', 'VRFCREQ_WAY', 'CP_ID', 'ARTICLE_TYPE'];
$sido = $this->model->getAreaList(); // 지역조회
$bonbu = $this->model->getBonbuList();
$team = $this->model->getTeamList();
$user = $this->model->getUserList();
$this->data['codes'] = $codes;
$this->data['sido'] = $sido;
$this->data['bonbu'] = $bonbu;
$this->data['team'] = $team;
$this->data['user'] = $user;
return view("pages/v2/m702/lists", $this->data);
} }
protected function getViewName(): string
public function getResultList()
{ {
$start = (int) $this->request->getGet('start') ?: 0; return 'pages/v2/m702/lists';
$end = (int) $this->request->getGet('length') ?: 10; }
$data = [ protected function getSearchKeys(): array
'atcl_no' => $this->request->getGet('atcl_no'), // 매물번호 {
'stat_cd' => $this->request->getGet('stat_cd'), // 현재상태 return [
'realtor_nm' => $this->request->getGet('realtor_nm'), // 중개소 'atcl_no',
'charger_gbn' => $this->request->getGet('charger_gbn'), // 배정여부 'stat_cd',
'assign_yn' => $this->request->getGet('assign_yn'), // 배정여부2 'realtor_nm',
'receipt_sdate' => $this->request->getGet('receipt_sdate'), // 접수기간1 'charger_gbn',
'receipt_edate' => $this->request->getGet('receipt_edate'), // 접수기간2 'assign_yn',
'complete_sdate' => $this->request->getGet('complete_sdate'), // 완료기간1 'receipt_sdate',
'complete_edate' => $this->request->getGet('complete_edate'), // 완료기간2 'receipt_edate',
'srcSido' => $this->request->getGet('srcSido'), // 시도 'complete_sdate',
'srcGugun' => $this->request->getGet('srcGugun'), // 시군구 'complete_edate',
'srcDong' => $this->request->getGet('srcDong'), // 읍면동 'srcSido',
'bonbu' => $this->request->getGet('bonbu'), // 본부 'srcGugun',
'team' => $this->request->getGet('team'), // 팀 'srcDong',
'damdang' => $this->request->getGet('damdang'), // 담당 'bonbu',
'vrfcreq_way' => $this->request->getGet('vrfcreq_way'), // 검증방식1 'team',
'vrfc_type_sub' => $this->request->getGet('vrfc_type_sub'), // 검증방식2 'damdang',
'rcpt_cpid' => $this->request->getGet('rcpt_cpid'), // 매체사 'vrfcreq_way',
'rlet_type_cd' => $this->request->getGet('rlet_type_cd'), // 매물종류 'vrfc_type_sub',
'corp_own' => $this->request->getGet('corp_own'), // 법인 'rcpt_cpid',
'rlet_type_cd',
'corp_own',
]; ];
$totalCount = $this->model->getTotalCount($data);
$datas = $this->model->getResultList($start, $end, $data);
return $this->response->setJSON(body: [
'recordsTotal' => $totalCount,
'recordsFiltered' => $totalCount,
'data' => $datas,
]);
} }
// 엑셀 다운로드
public function excel()
{
try {
$data = [
'atcl_no' => $this->request->getGet('atcl_no'), // 매물번호
'stat_cd' => $this->request->getGet('stat_cd'), // 현재상태
'realtor_nm' => $this->request->getGet('realtor_nm'), // 중개소
'charger_gbn' => $this->request->getGet('charger_gbn'), // 배정여부
'assign_yn' => $this->request->getGet('assign_yn'), // 배정여부2
'receipt_sdate' => $this->request->getGet('receipt_sdate'), // 접수기간1
'receipt_edate' => $this->request->getGet('receipt_edate'), // 접수기간2
'complete_sdate' => $this->request->getGet('complete_sdate'), // 완료기간1
'complete_edate' => $this->request->getGet('complete_edate'), // 완료기간2
'srcSido' => $this->request->getGet('srcSido'), // 시도
'srcGugun' => $this->request->getGet('srcGugun'), // 시군구
'srcDong' => $this->request->getGet('srcDong'), // 읍면동
'bonbu' => $this->request->getGet('bonbu'), // 본부
'team' => $this->request->getGet('team'), // 팀
'damdang' => $this->request->getGet('damdang'), // 담당
'vrfcreq_way' => $this->request->getGet('vrfcreq_way'), // 검증방식1
'vrfc_type_sub' => $this->request->getGet('vrfc_type_sub'), // 검증방식2
'rcpt_cpid' => $this->request->getGet('rcpt_cpid'), // 매체사
'rlet_type_cd' => $this->request->getGet('rlet_type_cd'), // 매물종류
'corp_own' => $this->request->getGet('corp_own'), // 법인
];
$datas = $this->model->getExcelList($data);
return $this->response->setJSON(body: [
'data' => $datas,
]);
} catch (\Exception $e) {
$e->getPrevious()->getTraceAsString();
}
}
// 배정변경
public function updateAssign() public function updateAssign()
{ {
try { try {

View File

@@ -1,129 +1,59 @@
<?php <?php
namespace App\Controllers\V2; namespace App\Controllers\V2;
use App\Controllers\BaseController; use App\Controllers\V2\BaseV2Controller;
use App\Libraries\NaverApiClient; use App\Libraries\NaverApiClient;
use App\Models\common\CodeModel;
use App\Models\results\M415Model; use App\Models\results\M415Model;
use App\Models\v2\M703Model;
use App\Models\v2\M710Model; use App\Models\v2\M710Model;
use App\Models\v2\M703Model;
class M703 extends BaseController class M703 extends BaseV2Controller
{ {
protected function createModel()
private $model, $codeModel;
public function __construct()
{ {
$this->model = new M703Model(); return new M703Model();
$this->codeModel = new CodeModel();
} }
public function lists() protected function getCodeKeys(): array
{ {
$codes = $this->codeModel->getCodeLists(['CP_ID', 'STEP_VERIFICATION', 'RECEIPT_STATUS3', 'FAX_CORP']); // 코드조회 return ['CP_ID', 'STEP_VERIFICATION', 'RECEIPT_STATUS3', 'FAX_CORP'];
$sido = $this->model->getAreaList(); // 지역조회
$bonbu = $this->model->getBonbuList();
$team = $this->model->getTeamList();
$user = $this->model->getUserList();
$this->data['sido'] = $sido;
$this->data['bonbu'] = $bonbu;
$this->data['team'] = $team;
$this->data['user'] = $user;
$this->data['codes'] = $codes;
return view("pages/v2/m703/lists", $this->data);
} }
public function getResultList() protected function getViewName(): string
{ {
$start = (int) $this->request->getGet('start') ?: 0; return 'pages/v2/m703/lists';
$end = (int) $this->request->getGet('length') ?: 10; }
$data = [ protected function getSearchKeys(): array
'atcl_no' => $this->request->getGet('atcl_no'), // 매물번호 {
'chk_atcl_no' => $this->request->getGet('chk_atcl_no'), // 매물번호입력 return [
'caller_no' => $this->request->getGet('caller_no'), // 발신팩스번호 'atcl_no',
'stat_cd' => $this->request->getGet('stat_cd'), // 현재상태 'chk_atcl_no',
'realtor_nm' => $this->request->getGet('realtor_nm'), // 중개소 'caller_no',
'charger_gbn' => $this->request->getGet('charger_gbn'), // 배정여부 'stat_cd',
'assign_yn' => $this->request->getGet('assign_yn'), // 배정여부2 'realtor_nm',
'receipt_sdate' => $this->request->getGet('receipt_sdate'), // 접수기간1 'charger_gbn',
'receipt_edate' => $this->request->getGet('receipt_edate'), // 접수기간2 'assign_yn',
'complete_sdate' => $this->request->getGet('complete_sdate'), // 완료기간1 'receipt_sdate',
'complete_edate' => $this->request->getGet('complete_edate'), // 완료기간2 'receipt_edate',
'srcSido' => $this->request->getGet('srcSido'), // 시도 'complete_sdate',
'srcGugun' => $this->request->getGet('srcGugun'), // 시군구 'complete_edate',
'srcDong' => $this->request->getGet('srcDong'), // 읍면동 'srcSido',
'bonbu' => $this->request->getGet('bonbu'), // 본부 'srcGugun',
'team' => $this->request->getGet('team'), // 팀 'srcDong',
'damdang' => $this->request->getGet('damdang'), // 담당 'bonbu',
'vrfcreq_way' => $this->request->getGet('vrfcreq_way'), // 검증방식1 'team',
'vrfc_type_sub' => $this->request->getGet('vrfc_type_sub'), // 검증방식2 'damdang',
'target_yn' => $this->request->getGet('target_yn'), // 홍보확인서여부 'vrfcreq_way',
'rcpt_cpid' => $this->request->getGet('rcpt_cpid'), // 매체사 'vrfc_type_sub',
'chk_rec' => $this->request->getGet('chk_rec'), // 동의서 유무 'target_yn',
'fax_corp' => $this->request->getGet('fax_corp'), // 팩스업체 'rcpt_cpid',
'chk_rec',
'fax_corp',
]; ];
$totalCount = $this->model->getTotalCount($data);
$datas = $this->model->getResultList($start, $end, $data);
return $this->response->setJSON(body: [
'recordsTotal' => $totalCount,
'recordsFiltered' => $totalCount,
'data' => $datas,
]);
} }
// 엑셀 다운로드
public function excel()
{
try {
$data = [
'atcl_no' => $this->request->getGet('atcl_no'), // 매물번호
'chk_atcl_no' => $this->request->getGet('chk_atcl_no'), // 매물번호입력
'caller_no' => $this->request->getGet('caller_no'), // 발신팩스번호
'stat_cd' => $this->request->getGet('stat_cd'), // 현재상태
'realtor_nm' => $this->request->getGet('realtor_nm'), // 중개소
'charger_gbn' => $this->request->getGet('charger_gbn'), // 배정여부
'assign_yn' => $this->request->getGet('assign_yn'), // 배정여부2
'receipt_sdate' => $this->request->getGet('receipt_sdate'), // 접수기간1
'receipt_edate' => $this->request->getGet('receipt_edate'), // 접수기간2
'complete_sdate' => $this->request->getGet('complete_sdate'), // 완료기간1
'complete_edate' => $this->request->getGet('complete_edate'), // 완료기간2
'srcSido' => $this->request->getGet('srcSido'), // 시도
'srcGugun' => $this->request->getGet('srcGugun'), // 시군구
'srcDong' => $this->request->getGet('srcDong'), // 읍면동
'bonbu' => $this->request->getGet('bonbu'), // 본부
'team' => $this->request->getGet('team'), // 팀
'damdang' => $this->request->getGet('damdang'), // 담당
'vrfcreq_way' => $this->request->getGet('vrfcreq_way'), // 검증방식1
'vrfc_type_sub' => $this->request->getGet('vrfc_type_sub'), // 검증방식2
'target_yn' => $this->request->getGet('target_yn'), // 홍보확인서여부
'rcpt_cpid' => $this->request->getGet('rcpt_cpid'), // 매체사
'chk_rec' => $this->request->getGet('chk_rec'), // 동의서 유무
'fax_corp' => $this->request->getGet('fax_corp'), // 팩스업체
];
$datas = $this->model->getExcelList($data);
return $this->response->setJSON(body: [
'data' => $datas,
]);
} catch (\Exception $e) {
$e->getPrevious()->getTraceAsString();
}
}
// 상세화면
public function detail($id) public function detail($id)
{ {
$naver = new NaverApiClient(); $naver = new NaverApiClient();

View File

@@ -1,128 +1,60 @@
<?php <?php
namespace App\Controllers\V2; namespace App\Controllers\V2;
use App\Controllers\BaseController; use App\Controllers\V2\BaseV2Controller;
use App\Libraries\MyUpload; use App\Libraries\MyUpload;
use App\Libraries\NaverApiClient; use App\Libraries\NaverApiClient;
use App\Models\common\CodeModel;
use App\Models\results\M415Model; use App\Models\results\M415Model;
use App\Models\v2\M704Model;
use App\Models\v2\M710Model; use App\Models\v2\M710Model;
use Exception; use Exception;
use App\Models\v2\M704Model;
class M704 extends BaseController class M704 extends BaseV2Controller
{ {
private $model, $codeModel; protected function createModel()
public function __construct()
{ {
$this->model = new M704Model(); return new M704Model();
$this->codeModel = new CodeModel();
} }
public function lists(): string protected function getCodeKeys(): array
{ {
$codes = $this->codeModel->getCodeLists(['STEP_VERIFICATION', 'VRFCREQ_WAY', 'CP_ID', 'TEL_FAIL_CAUSE', 'ARTICLE_TYPE']); // 코드조회 return ['STEP_VERIFICATION', 'VRFCREQ_WAY', 'CP_ID', 'TEL_FAIL_CAUSE', 'ARTICLE_TYPE'];
$sido = $this->model->getAreaList(); // 지역조회
$bonbu = $this->model->getBonbuList();
$team = $this->model->getTeamList();
$user = $this->model->getUserList();
$this->data['sido'] = $sido;
$this->data['bonbu'] = $bonbu;
$this->data['team'] = $team;
$this->data['user'] = $user;
$this->data['codes'] = $codes;
return view("pages/v2/m704/lists", $this->data);
} }
protected function getViewName(): string
public function getResultList()
{ {
$start = (int) $this->request->getGet('start') ?: 0; return 'pages/v2/m704/lists';
$end = (int) $this->request->getGet('length') ?: 10; }
$data = [ protected function getSearchKeys(): array
'atcl_no' => $this->request->getGet('atcl_no'), // 매물번호 {
'stat_cd' => $this->request->getGet('stat_cd'), // 현재상태 return [
'realtor_nm' => $this->request->getGet('realtor_nm'), // 중개소 'atcl_no',
'charger_gbn' => $this->request->getGet('charger_gbn'), // 배정여부 'stat_cd',
'assign_yn' => $this->request->getGet('assign_yn'), // 배정여부2 'realtor_nm',
'receipt_sdate' => $this->request->getGet('receipt_sdate'), // 접수기간1 'charger_gbn',
'receipt_edate' => $this->request->getGet('receipt_edate'), // 접수기간2 'assign_yn',
'stat_complete_date' => $this->request->getGet('stat_complete_date'), // 진행상태별시간유형 'receipt_sdate',
'complete_sdate' => $this->request->getGet('complete_sdate'), // 진행상태별시간1 'receipt_edate',
'complete_edate' => $this->request->getGet('complete_edate'), // 진행상태별시간2 'stat_complete_date',
'srcSido' => $this->request->getGet('srcSido'), // 시도 'complete_sdate',
'srcGugun' => $this->request->getGet('srcGugun'), // 시군구 'complete_edate',
'srcDong' => $this->request->getGet('srcDong'), // 읍면동 'srcSido',
'bonbu' => $this->request->getGet('bonbu'), // 본부 'srcGugun',
'team' => $this->request->getGet('team'), // 팀 'srcDong',
'damdang' => $this->request->getGet('damdang'), // 담당 'bonbu',
'vrfcreq_way' => $this->request->getGet('vrfcreq_way'), // 검증방식1 'team',
'vrfc_type_sub' => $this->request->getGet('vrfc_type_sub'), // 검증방식2 'damdang',
'rcpt_cpid' => $this->request->getGet('rcpt_cpid'), // 매체사 'vrfcreq_way',
'rlet_type_cd' => $this->request->getGet('rlet_type_cd'), // 매물종류 'vrfc_type_sub',
'reference_file_url_yn' => $this->request->getGet('reference_file_url_yn'), // 참고용 'rcpt_cpid',
'corp_own' => $this->request->getGet('corp_own'), // 법인 'rlet_type_cd',
'reference_file_url_yn',
'corp_own',
]; ];
$totalCount = $this->model->getTotalCount($data);
$datas = $this->model->getResultList($start, $end, $data);
return $this->response->setJSON(body: [
'recordsTotal' => $totalCount,
'recordsFiltered' => $totalCount,
'data' => $datas,
]);
} }
// 엑셀 다운로드
public function excel()
{
try {
$data = [
'atcl_no' => $this->request->getGet('atcl_no'), // 매물번호
'stat_cd' => $this->request->getGet('stat_cd'), // 현재상태
'realtor_nm' => $this->request->getGet('realtor_nm'), // 중개소
'charger_gbn' => $this->request->getGet('charger_gbn'), // 배정여부
'assign_yn' => $this->request->getGet('assign_yn'), // 배정여부2
'receipt_sdate' => $this->request->getGet('receipt_sdate'), // 접수기간1
'receipt_edate' => $this->request->getGet('receipt_edate'), // 접수기간2
'stat_complete_date' => $this->request->getGet('stat_complete_date'), // 진행상태별시간유형
'complete_sdate' => $this->request->getGet('complete_sdate'), // 진행상태별시간1
'complete_edate' => $this->request->getGet('complete_edate'), // 진행상태별시간2
'srcSido' => $this->request->getGet('srcSido'), // 시도
'srcGugun' => $this->request->getGet('srcGugun'), // 시군구
'srcDong' => $this->request->getGet('srcDong'), // 읍면동
'bonbu' => $this->request->getGet('bonbu'), // 본부
'team' => $this->request->getGet('team'), // 팀
'damdang' => $this->request->getGet('damdang'), // 담당
'vrfcreq_way' => $this->request->getGet('vrfcreq_way'), // 검증방식1
'vrfc_type_sub' => $this->request->getGet('vrfc_type_sub'), // 검증방식2
'rcpt_cpid' => $this->request->getGet('rcpt_cpid'), // 매체사
'rlet_type_cd' => $this->request->getGet('rlet_type_cd'), // 매물종류
'reference_file_url_yn' => $this->request->getGet('reference_file_url_yn'), // 참고용
'corp_own' => $this->request->getGet('corp_own'), // 법인
];
$datas = $this->model->getExcelList($data);
return $this->response->setJSON(body: [
'data' => $datas,
]);
} catch (\Exception $e) {
$e->getPrevious()->getTraceAsString();
}
}
// 상세화면
public function detail($id) public function detail($id)
{ {
$naver = new NaverApiClient(); $naver = new NaverApiClient();

View File

@@ -1,86 +1,60 @@
<?php <?php
namespace App\Controllers\V2; namespace App\Controllers\V2;
use App\Controllers\BaseController; use App\Controllers\V2\BaseV2Controller;
use App\Libraries\Common; use App\Libraries\Common;
use App\Libraries\MyUpload; use App\Libraries\MyUpload;
use App\Libraries\NaverApiClient; use App\Libraries\NaverApiClient;
use App\Models\common\CodeModel;
use App\Models\results\M415Model; use App\Models\results\M415Model;
use App\Models\v2\M705Model;
use App\Models\v2\M710Model; use App\Models\v2\M710Model;
use App\Models\v2\M705Model;
class M705 extends BaseController class M705 extends BaseV2Controller
{ {
protected function createModel()
private $model, $codeModel;
public function __construct()
{ {
$this->model = new M705Model(); return new M705Model();
$this->codeModel = new CodeModel();
} }
public function lists(): string protected function getCodeKeys(): array
{ {
$codes = $this->codeModel->getCodeLists(['STEP_VERIFICATION', 'VRFCREQ_WAY', 'CP_ID', 'TEL_FAIL_CAUSE', 'ARTICLE_TYPE']); // 코드조회 return ['STEP_VERIFICATION', 'VRFCREQ_WAY', 'CP_ID', 'TEL_FAIL_CAUSE', 'ARTICLE_TYPE'];
$sido = $this->model->getAreaList(); // 지역조회
$bonbu = $this->model->getBonbuList();
$team = $this->model->getTeamList();
$user = $this->model->getUserList();
$this->data['sido'] = $sido;
$this->data['bonbu'] = $bonbu;
$this->data['team'] = $team;
$this->data['user'] = $user;
$this->data['codes'] = $codes;
return view("pages/v2/m705/lists", $this->data);
} }
public function getResultList() protected function getViewName(): string
{ {
$start = (int) $this->request->getGet('start') ?: 0; return 'pages/v2/m705/lists';
$end = (int) $this->request->getGet('length') ?: 10; }
$data = [ protected function getSearchKeys(): array
'atcl_no' => $this->request->getGet('atcl_no'), // 매물번호 {
'stat_cd' => $this->request->getGet('stat_cd'), // 현재상태 return [
'realtor_nm' => $this->request->getGet('realtor_nm'), // 중개소 'atcl_no',
'charger_gbn' => $this->request->getGet('charger_gbn'), // 배정여부 'stat_cd',
'assign_yn' => $this->request->getGet('assign_yn'), // 배정여부2 'realtor_nm',
'receipt_sdate' => $this->request->getGet('receipt_sdate'), // 접수기간1 'charger_gbn',
'receipt_edate' => $this->request->getGet('receipt_edate'), // 접수기간2 'assign_yn',
'complete_sdate' => $this->request->getGet('complete_sdate'), // 완료기간1 'receipt_sdate',
'complete_edate' => $this->request->getGet('complete_edate'), // 완료기간2 'receipt_edate',
'srcSido' => $this->request->getGet('srcSido'), // 시도 'complete_sdate',
'srcGugun' => $this->request->getGet('srcGugun'), // 시군구 'complete_edate',
'srcDong' => $this->request->getGet('srcDong'), // 읍면동 'srcSido',
'bonbu' => $this->request->getGet('bonbu'), // 본부 'srcGugun',
'team' => $this->request->getGet('team'), // 팀 'srcDong',
'damdang' => $this->request->getGet('damdang'), // 담당 'bonbu',
'vrfcreq_way' => $this->request->getGet('vrfcreq_way'), // 검증방식1 'team',
'vrfc_type_sub' => $this->request->getGet('vrfc_type_sub'), // 검증방식2 'damdang',
'rcpt_cpid' => $this->request->getGet('rcpt_cpid'), // 매체사 'vrfcreq_way',
'rlet_type_cd' => $this->request->getGet('rlet_type_cd'), // 매물종류 'vrfc_type_sub',
'chk_spc_yn' => $this->request->getGet('chk_spc_yn'), // 면적여부 'rcpt_cpid',
'reference_file_url_yn' => $this->request->getGet('reference_file_url_yn'), // 참고용 'rlet_type_cd',
'corp_own' => $this->request->getGet('corp_own'), // 법인 'chk_spc_yn',
'reference_file_url_yn',
'corp_own',
]; ];
$totalCount = $this->model->getTotalCount($data);
$datas = $this->model->getResultList($start, $end, $data);
return $this->response->setJSON(body: [
'recordsTotal' => $totalCount,
'recordsFiltered' => $totalCount,
'data' => $datas,
]);
} }
// 배정확인
public function getNotAssign() public function getNotAssign()
{ {
try { try {

View File

@@ -1,117 +1,54 @@
<?php <?php
namespace App\Controllers\V2; namespace App\Controllers\V2;
use App\Controllers\BaseController; use App\Controllers\V2\BaseV2Controller;
use App\Libraries\NaverApiClient; use App\Libraries\NaverApiClient;
use App\Models\common\CodeModel;
use App\Models\results\M415Model; use App\Models\results\M415Model;
use App\Models\v2\M706Model;
use App\Models\v2\M710Model; use App\Models\v2\M710Model;
use Exception; use Exception;
use App\Models\v2\M706Model;
class M706 extends BaseController class M706 extends BaseV2Controller
{ {
private $model, $codeModel; protected function createModel()
public function __construct()
{ {
$this->model = new M706Model(); return new M706Model();
$this->codeModel = new CodeModel();
} }
public function lists(): string protected function getCodeKeys(): array
{ {
$codes = $this->codeModel->getCodeLists(['STEP_VERIFICATION', 'VRFCREQ_WAY', 'CP_ID']); // 코드조회 return ['STEP_VERIFICATION', 'VRFCREQ_WAY', 'CP_ID'];
$sido = $this->model->getAreaList(); // 지역조회
$bonbu = $this->model->getBonbuList();
$team = $this->model->getTeamList();
$user = $this->model->getUserList();
$this->data['sido'] = $sido;
$this->data['bonbu'] = $bonbu;
$this->data['team'] = $team;
$this->data['user'] = $user;
$this->data['codes'] = $codes;
return view("pages/v2/m706/lists", $this->data);
} }
public function getResultList() protected function getViewName(): string
{ {
$start = (int) $this->request->getGet('start') ?: 0; return 'pages/v2/m706/lists';
$end = (int) $this->request->getGet('length') ?: 10; }
$data = [ protected function getSearchKeys(): array
'atcl_no' => $this->request->getGet('atcl_no'), // 매물번호 {
'stat_cd' => $this->request->getGet('stat_cd'), // 현재상태 return [
'realtor_nm' => $this->request->getGet('realtor_nm'), // 중개소 'atcl_no',
'charger_gbn' => $this->request->getGet('charger_gbn'), // 배정여부 'stat_cd',
'assign_yn' => $this->request->getGet('assign_yn'), // 배정여부2 'realtor_nm',
'receipt_sdate' => $this->request->getGet('receipt_sdate'), // 접수기간1 'charger_gbn',
'receipt_edate' => $this->request->getGet('receipt_edate'), // 접수기간2 'assign_yn',
'complete_sdate' => $this->request->getGet('complete_sdate'), // 완료기간1 'receipt_sdate',
'complete_edate' => $this->request->getGet('complete_edate'), // 완료기간2 'receipt_edate',
'srcSido' => $this->request->getGet('srcSido'), // 시도 'complete_sdate',
'srcGugun' => $this->request->getGet('srcGugun'), // 시군구 'complete_edate',
'srcDong' => $this->request->getGet('srcDong'), // 읍면동 'srcSido',
'bonbu' => $this->request->getGet('bonbu'), // 본부 'srcGugun',
'team' => $this->request->getGet('team'), // 팀 'srcDong',
'damdang' => $this->request->getGet('damdang'), // 담당 'bonbu',
'vrfcreq_way' => $this->request->getGet('vrfcreq_way'), // 검증방식1 'team',
'rcpt_cpid' => $this->request->getGet('rcpt_cpid'), // 매체사 'damdang',
'vrfcreq_way',
'rcpt_cpid',
]; ];
$totalCount = $this->model->getTotalCount($data);
$datas = $this->model->getResultList($start, $end, $data);
return $this->response->setJSON(body: [
'recordsTotal' => $totalCount,
'recordsFiltered' => $totalCount,
'data' => $datas,
]);
} }
// 엑셀 다운로드
public function excel()
{
try {
$data = [
'atcl_no' => $this->request->getGet('atcl_no'), // 매물번호
'stat_cd' => $this->request->getGet('stat_cd'), // 현재상태
'realtor_nm' => $this->request->getGet('realtor_nm'), // 중개소
'charger_gbn' => $this->request->getGet('charger_gbn'), // 배정여부
'assign_yn' => $this->request->getGet('assign_yn'), // 배정여부2
'receipt_sdate' => $this->request->getGet('receipt_sdate'), // 접수기간1
'receipt_edate' => $this->request->getGet('receipt_edate'), // 접수기간2
'complete_sdate' => $this->request->getGet('complete_sdate'), // 완료기간1
'complete_edate' => $this->request->getGet('complete_edate'), // 완료기간2
'srcSido' => $this->request->getGet('srcSido'), // 시도
'srcGugun' => $this->request->getGet('srcGugun'), // 시군구
'srcDong' => $this->request->getGet('srcDong'), // 읍면동
'bonbu' => $this->request->getGet('bonbu'), // 본부
'team' => $this->request->getGet('team'), // 팀
'damdang' => $this->request->getGet('damdang'), // 담당
'vrfcreq_way' => $this->request->getGet('vrfcreq_way'), // 검증방식1
'rcpt_cpid' => $this->request->getGet('rcpt_cpid'), // 매체사
];
$datas = $this->model->getExcelList($data);
return $this->response->setJSON(body: [
'data' => $datas,
]);
} catch (\Exception $e) {
$e->getPrevious()->getTraceAsString();
}
}
// 상세화면
public function detail($id) public function detail($id)
{ {
$naver = new NaverApiClient(); $naver = new NaverApiClient();

View File

@@ -1,125 +1,59 @@
<?php <?php
namespace App\Controllers\V2; namespace App\Controllers\V2;
use App\Controllers\BaseController; use App\Controllers\V2\BaseV2Controller;
use App\Libraries\Common; use App\Libraries\Common;
use App\Libraries\NaverApiClient; use App\Libraries\NaverApiClient;
use App\Models\common\CodeModel;
use App\Models\results\M415Model; use App\Models\results\M415Model;
use App\Models\v2\M708Model;
use App\Models\v2\M710Model; use App\Models\v2\M710Model;
use Exception; use Exception;
use App\Models\v2\M708Model;
class M708 extends BaseController class M708 extends BaseV2Controller
{ {
private $model, $codeModel; protected function createModel()
public function __construct()
{ {
$this->model = new M708Model(); return new M708Model();
$this->codeModel = new CodeModel();
} }
public function lists(): string protected function getCodeKeys(): array
{ {
$codes = $this->codeModel->getCodeLists(['CP_ID', 'STEP_VERIFICATION', 'RECEIPT_STATUS3', 'ARTICLE_TYPE']); // 코드조회 return ['CP_ID', 'STEP_VERIFICATION', 'RECEIPT_STATUS3', 'ARTICLE_TYPE'];
$sido = $this->model->getAreaList(); // 지역조회
$bonbu = $this->model->getBonbuList();
$team = $this->model->getTeamList();
$user = $this->model->getUserList();
$this->data['sido'] = $sido;
$this->data['bonbu'] = $bonbu;
$this->data['team'] = $team;
$this->data['user'] = $user;
$this->data['codes'] = $codes;
return view("pages/v2/m708/lists", $this->data);
} }
public function getResultList() protected function getViewName(): string
{ {
$start = (int) $this->request->getGet('start') ?: 0; return 'pages/v2/m708/lists';
$end = (int) $this->request->getGet('length') ?: 10; }
$data = [ protected function getSearchKeys(): array
'atcl_no' => $this->request->getGet('atcl_no'), // 매물번호 {
'chk_atcl_no' => $this->request->getGet('chk_atcl_no'), // 매물번호입력 return [
'caller_no' => $this->request->getGet('caller_no'), // 발신팩스번호 'atcl_no',
'stat_cd' => $this->request->getGet('stat_cd'), // 현재상태 'chk_atcl_no',
'realtor_nm' => $this->request->getGet('realtor_nm'), // 중개소 'caller_no',
'charger_gbn' => $this->request->getGet('charger_gbn'), // 배정여부 'stat_cd',
'assign_yn' => $this->request->getGet('assign_yn'), // 배정여부2 'realtor_nm',
'receipt_sdate' => $this->request->getGet('receipt_sdate'), // 접수기간1 'charger_gbn',
'receipt_edate' => $this->request->getGet('receipt_edate'), // 접수기간2 'assign_yn',
'complete_sdate' => $this->request->getGet('complete_sdate'), // 완료기간1 'receipt_sdate',
'complete_edate' => $this->request->getGet('complete_edate'), // 완료기간2 'receipt_edate',
'srcSido' => $this->request->getGet('srcSido'), // 시도 'complete_sdate',
'srcGugun' => $this->request->getGet('srcGugun'), // 시군구 'complete_edate',
'srcDong' => $this->request->getGet('srcDong'), // 읍면동 'srcSido',
'bonbu' => $this->request->getGet('bonbu'), // 본부 'srcGugun',
'team' => $this->request->getGet('team'), // 팀 'srcDong',
'damdang' => $this->request->getGet('damdang'), // 담당 'bonbu',
'target_yn' => $this->request->getGet('target_yn'), // 홍보확인서여부 'team',
'rcpt_cpid' => $this->request->getGet('rcpt_cpid'), // 매체사 'damdang',
'rlet_type_cd' => $this->request->getGet('rlet_type_cd'), // 매물종류 'target_yn',
'rcpt_v2' => $this->request->getGet('rcpt_v2'), // 검증방식 'rcpt_cpid',
'rlet_type_cd',
'rcpt_v2',
]; ];
$totalCount = $this->model->getTotalCount($data);
$datas = $this->model->getResultList($start, $end, $data);
return $this->response->setJSON(body: [
'recordsTotal' => $totalCount,
'recordsFiltered' => $totalCount,
'data' => $datas,
]);
} }
// 엑셀 다운로드
public function excel()
{
try {
$data = [
'atcl_no' => $this->request->getGet('atcl_no'), // 매물번호
'chk_atcl_no' => $this->request->getGet('chk_atcl_no'), // 매물번호입력
'caller_no' => $this->request->getGet('caller_no'), // 발신팩스번호
'stat_cd' => $this->request->getGet('stat_cd'), // 현재상태
'realtor_nm' => $this->request->getGet('realtor_nm'), // 중개소
'charger_gbn' => $this->request->getGet('charger_gbn'), // 배정여부
'assign_yn' => $this->request->getGet('assign_yn'), // 배정여부2
'receipt_sdate' => $this->request->getGet('receipt_sdate'), // 접수기간1
'receipt_edate' => $this->request->getGet('receipt_edate'), // 접수기간2
'complete_sdate' => $this->request->getGet('complete_sdate'), // 완료기간1
'complete_edate' => $this->request->getGet('complete_edate'), // 완료기간2
'srcSido' => $this->request->getGet('srcSido'), // 시도
'srcGugun' => $this->request->getGet('srcGugun'), // 시군구
'srcDong' => $this->request->getGet('srcDong'), // 읍면동
'bonbu' => $this->request->getGet('bonbu'), // 본부
'team' => $this->request->getGet('team'), // 팀
'damdang' => $this->request->getGet('damdang'), // 담당
'target_yn' => $this->request->getGet('target_yn'), // 홍보확인서여부
'rcpt_cpid' => $this->request->getGet('rcpt_cpid'), // 매체사
'rlet_type_cd' => $this->request->getGet('rlet_type_cd'), // 매물종류
'rcpt_v2' => $this->request->getGet('rcpt_v2'), // 검증방식
];
$datas = $this->model->getExcelList($data);
return $this->response->setJSON(body: [
'data' => $datas,
]);
} catch (\Exception $e) {
$e->getPrevious()->getTraceAsString();
}
}
// 상세화면
public function detail($id) public function detail($id)
{ {
$id = (string) $id; $id = (string) $id;

View File

@@ -1,120 +1,55 @@
<?php <?php
namespace App\Controllers\V2; namespace App\Controllers\V2;
use App\Controllers\BaseController; use App\Controllers\V2\BaseV2Controller;
use App\Libraries\Common; use App\Libraries\Common;
use App\Models\common\CodeModel;
use App\Models\v2\M709Model; use App\Models\v2\M709Model;
class M709 extends BaseController class M709 extends BaseV2Controller
{ {
private $model, $codeModel; protected function createModel()
public function __construct()
{ {
$this->model = new M709Model(); return new M709Model();
$this->codeModel = new CodeModel();
} }
public function lists(): string protected function getCodeKeys(): array
{ {
$codes = $this->codeModel->getCodeLists(['CP_ID', 'STEP_VERIFICATION', 'RECEIPT_STATUS3', 'ARTICLE_TYPE']); // 코드조회 return ['CP_ID', 'STEP_VERIFICATION', 'RECEIPT_STATUS3', 'ARTICLE_TYPE'];
$sido = $this->model->getAreaList(); // 지역조회
$bonbu = $this->model->getBonbuList();
$team = $this->model->getTeamList();
$user = $this->model->getUserList();
$this->data['sido'] = $sido;
$this->data['bonbu'] = $bonbu;
$this->data['team'] = $team;
$this->data['user'] = $user;
$this->data['codes'] = $codes;
return view("pages/v2/m709/lists", $this->data);
} }
protected function getViewName(): string
public function getResultList()
{ {
$start = (int) $this->request->getGet('start') ?: 0; return 'pages/v2/m709/lists';
$end = (int) $this->request->getGet('length') ?: 10; }
$data = [ protected function getSearchKeys(): array
'atcl_no' => $this->request->getGet('atcl_no'), // 매물번호 {
'chk_atcl_no' => $this->request->getGet('chk_atcl_no'), // 매물번호입력 return [
'caller_no' => $this->request->getGet('caller_no'), // 발신팩스번호 'atcl_no',
'stat_cd' => $this->request->getGet('stat_cd'), // 현재상태 'chk_atcl_no',
'realtor_nm' => $this->request->getGet('realtor_nm'), // 중개소 'caller_no',
'charger_gbn' => $this->request->getGet('charger_gbn'), // 배정여부 'stat_cd',
'assign_yn' => $this->request->getGet('assign_yn'), // 배정여부2 'realtor_nm',
'receipt_sdate' => $this->request->getGet('receipt_sdate'), // 접수기간1 'charger_gbn',
'receipt_edate' => $this->request->getGet('receipt_edate'), // 접수기간2 'assign_yn',
'complete_sdate' => $this->request->getGet('complete_sdate'), // 완료기간1 'receipt_sdate',
'complete_edate' => $this->request->getGet('complete_edate'), // 완료기간2 'receipt_edate',
'srcSido' => $this->request->getGet('srcSido'), // 시도 'complete_sdate',
'srcGugun' => $this->request->getGet('srcGugun'), // 시군구 'complete_edate',
'srcDong' => $this->request->getGet('srcDong'), // 읍면동 'srcSido',
'bonbu' => $this->request->getGet('bonbu'), // 본부 'srcGugun',
'team' => $this->request->getGet('team'), // 팀 'srcDong',
'damdang' => $this->request->getGet('damdang'), // 담당 'bonbu',
'target_yn' => $this->request->getGet('target_yn'), // 홍보확인서여부 'team',
'rcpt_cpid' => $this->request->getGet('rcpt_cpid'), // 매체사 'damdang',
'rlet_type_cd' => $this->request->getGet('rlet_type_cd'), // 매물종류 'target_yn',
'rcpt_v2' => $this->request->getGet('rcpt_v2'), // 검증방식 'rcpt_cpid',
'rlet_type_cd',
'rcpt_v2',
]; ];
$totalCount = $this->model->getTotalCount($data);
$datas = $this->model->getResultList($start, $end, $data);
return $this->response->setJSON(body: [
'recordsTotal' => $totalCount,
'recordsFiltered' => $totalCount,
'data' => $datas,
]);
} }
public function excel()
{
try {
$data = [
'atcl_no' => $this->request->getGet('atcl_no'), // 매물번호
'chk_atcl_no' => $this->request->getGet('chk_atcl_no'), // 매물번호입력
'caller_no' => $this->request->getGet('caller_no'), // 발신팩스번호
'stat_cd' => $this->request->getGet('stat_cd'), // 현재상태
'realtor_nm' => $this->request->getGet('realtor_nm'), // 중개소
'charger_gbn' => $this->request->getGet('charger_gbn'), // 배정여부
'assign_yn' => $this->request->getGet('assign_yn'), // 배정여부2
'receipt_sdate' => $this->request->getGet('receipt_sdate'), // 접수기간1
'receipt_edate' => $this->request->getGet('receipt_edate'), // 접수기간2
'complete_sdate' => $this->request->getGet('complete_sdate'), // 완료기간1
'complete_edate' => $this->request->getGet('complete_edate'), // 완료기간2
'srcSido' => $this->request->getGet('srcSido'), // 시도
'srcGugun' => $this->request->getGet('srcGugun'), // 시군구
'srcDong' => $this->request->getGet('srcDong'), // 읍면동
'bonbu' => $this->request->getGet('bonbu'), // 본부
'team' => $this->request->getGet('team'), // 팀
'damdang' => $this->request->getGet('damdang'), // 담당
'target_yn' => $this->request->getGet('target_yn'), // 홍보확인서여부
'rcpt_cpid' => $this->request->getGet('rcpt_cpid'), // 매체사
'rlet_type_cd' => $this->request->getGet('rlet_type_cd'), // 매물종류
'rcpt_v2' => $this->request->getGet('rcpt_v2'), // 검증방식
];
$datas = $this->model->getExcelList($data);
return $this->response->setJSON(body: [
'data' => $datas,
]);
} catch (\Exception $e) {
$e->getPrevious()->getTraceAsString();
}
}
// 상세화면
public function detail($id) public function detail($id)
{ {
$id = (string) $id; $id = (string) $id;

View File

@@ -18,6 +18,7 @@ class M710 extends BaseController
public function lists(): string public function lists(): string
{ {
$codes = $this->codeModel->getCodeLists(['STEP_VERIFICATION', 'VRFCREQ_WAY', 'CP_ID', 'ARTICLE_TYPE']); // 코드조회 $codes = $this->codeModel->getCodeLists(['STEP_VERIFICATION', 'VRFCREQ_WAY', 'CP_ID', 'ARTICLE_TYPE']); // 코드조회
$sido = $this->model->getAreaList(); // 지역조회 $sido = $this->model->getAreaList(); // 지역조회
$bonbu = $this->model->getBonbuList(); $bonbu = $this->model->getBonbuList();
$team = $this->model->getTeamList(); $team = $this->model->getTeamList();

View File

@@ -1,128 +1,59 @@
<?php <?php
namespace App\Controllers\V2; namespace App\Controllers\V2;
use App\Controllers\BaseController; use App\Controllers\V2\BaseV2Controller;
use App\Libraries\Common; use App\Libraries\Common;
use App\Libraries\MyUpload; use App\Libraries\MyUpload;
use App\Libraries\NaverApiClient; use App\Libraries\NaverApiClient;
use App\Models\common\CodeModel;
use App\Models\results\M415Model; use App\Models\results\M415Model;
use App\Models\v2\M712Model; use App\Models\v2\M712Model;
class M712 extends BaseController class M712 extends BaseV2Controller
{ {
private $model, $codeModel; protected function createModel()
public function __construct()
{ {
$this->model = new M712Model(); return new M712Model();
$this->codeModel = new CodeModel();
} }
public function lists(): string protected function getCodeKeys(): array
{ {
$codes = $this->codeModel->getCodeLists(['STEP_VERIFICATION', 'VRFCREQ_WAY', 'CP_ID', 'ARTICLE_TYPE']); // 코드조회 return ['STEP_VERIFICATION', 'VRFCREQ_WAY', 'CP_ID', 'ARTICLE_TYPE'];
$sido = $this->model->getAreaList(); // 지역조회
$bonbu = $this->model->getBonbuList();
$team = $this->model->getTeamList();
$user = $this->model->getUserList();
$this->data['sido'] = $sido;
$this->data['bonbu'] = $bonbu;
$this->data['team'] = $team;
$this->data['user'] = $user;
$this->data['codes'] = $codes;
return view("pages/v2/m712/lists", $this->data);
} }
protected function getViewName(): string
public function getResultList()
{ {
$start = (int) $this->request->getGet('start') ?: 0; return 'pages/v2/m712/lists';
$end = (int) $this->request->getGet('length') ?: 10; }
$data = [ protected function getSearchKeys(): array
'atcl_no' => $this->request->getGet('atcl_no'), // 매물번호 {
'stat_cd' => $this->request->getGet('stat_cd'), // 현재상태 return [
'realtor_nm' => $this->request->getGet('realtor_nm'), // 중개소 'atcl_no',
'charger_gbn' => $this->request->getGet('charger_gbn'), // 배정여부 'stat_cd',
'assign_yn' => $this->request->getGet('assign_yn'), // 배정여부2 'realtor_nm',
'receipt_sdate' => $this->request->getGet('receipt_sdate'), // 접수기간1 'charger_gbn',
'receipt_edate' => $this->request->getGet('receipt_edate'), // 접수기간2 'assign_yn',
'complete_sdate' => $this->request->getGet('complete_sdate'), // 완료기간1 'receipt_sdate',
'complete_edate' => $this->request->getGet('complete_edate'), // 완료기간2 'receipt_edate',
'srcSido' => $this->request->getGet('srcSido'), // 시도 'complete_sdate',
'srcGugun' => $this->request->getGet('srcGugun'), // 시군구 'complete_edate',
'srcDong' => $this->request->getGet('srcDong'), // 읍면동 'srcSido',
'bonbu' => $this->request->getGet('bonbu'), // 본부 'srcGugun',
'team' => $this->request->getGet('team'), // 팀 'srcDong',
'damdang' => $this->request->getGet('damdang'), // 담당 'bonbu',
'vrfcreq_way' => $this->request->getGet('vrfcreq_way'), // 검증방식1 'team',
'vrfc_type_sub' => $this->request->getGet('vrfc_type_sub'), // 검증방식2 'damdang',
'rcpt_cpid' => $this->request->getGet('rcpt_cpid'), // 매체사 'vrfcreq_way',
'rlet_type_cd' => $this->request->getGet('rlet_type_cd'), // 매물종류 'vrfc_type_sub',
'reference_file_url_yn' => $this->request->getGet('reference_file_url_yn'), // 참고용 'rcpt_cpid',
'ownerTypeCode' => $this->request->getGet('ownerTypeCode'), // 소유자 구분 'rlet_type_cd',
'document_not_received_yn' => $this->request->getGet('document_not_received_yn'), // 서류미수취 'reference_file_url_yn',
'ownerTypeCode',
'document_not_received_yn',
]; ];
$totalCount = $this->model->getTotalCount($data);
$datas = $this->model->getResultList($start, $end, $data);
return $this->response->setJSON(body: [
'recordsTotal' => $totalCount,
'recordsFiltered' => $totalCount,
'data' => $datas,
]);
} }
// 엑셀 다운로드
public function excel()
{
try {
$data = [
'atcl_no' => $this->request->getGet('atcl_no'), // 매물번호
'stat_cd' => $this->request->getGet('stat_cd'), // 현재상태
'realtor_nm' => $this->request->getGet('realtor_nm'), // 중개소
'charger_gbn' => $this->request->getGet('charger_gbn'), // 배정여부
'assign_yn' => $this->request->getGet('assign_yn'), // 배정여부2
'receipt_sdate' => $this->request->getGet('receipt_sdate'), // 접수기간1
'receipt_edate' => $this->request->getGet('receipt_edate'), // 접수기간2
'complete_sdate' => $this->request->getGet('complete_sdate'), // 완료기간1
'complete_edate' => $this->request->getGet('complete_edate'), // 완료기간2
'srcSido' => $this->request->getGet('srcSido'), // 시도
'srcGugun' => $this->request->getGet('srcGugun'), // 시군구
'srcDong' => $this->request->getGet('srcDong'), // 읍면동
'bonbu' => $this->request->getGet('bonbu'), // 본부
'team' => $this->request->getGet('team'), // 팀
'damdang' => $this->request->getGet('damdang'), // 담당
'vrfcreq_way' => $this->request->getGet('vrfcreq_way'), // 검증방식1
'vrfc_type_sub' => $this->request->getGet('vrfc_type_sub'), // 검증방식2
'rcpt_cpid' => $this->request->getGet('rcpt_cpid'), // 매체사
'rlet_type_cd' => $this->request->getGet('rlet_type_cd'), // 매물종류
'reference_file_url_yn' => $this->request->getGet('reference_file_url_yn'), // 참고용
'ownerTypeCode' => $this->request->getGet('ownerTypeCode'), // 소유자 구분
'document_not_received_yn' => $this->request->getGet('document_not_received_yn'), // 서류미수취
];
$datas = $this->model->getExcelList($data);
return $this->response->setJSON(body: [
'data' => $datas,
]);
} catch (\Exception $e) {
$e->getPrevious()->getTraceAsString();
}
}
// 상세화면
public function detail($id): string public function detail($id): string
{ {

View File

@@ -1,127 +1,59 @@
<?php <?php
namespace App\Controllers\V2; namespace App\Controllers\V2;
use App\Controllers\BaseController; use App\Controllers\V2\BaseV2Controller;
use App\Libraries\MyUpload; use App\Libraries\MyUpload;
use App\Libraries\NaverApiClient; use App\Libraries\NaverApiClient;
use App\Models\common\CodeModel;
use App\Models\results\M415Model; use App\Models\results\M415Model;
use App\Models\v2\M710Model; use App\Models\v2\M710Model;
use App\Models\v2\M713Model; use App\Models\v2\M713Model;
class M713 extends BaseController class M713 extends BaseV2Controller
{ {
private $model, $codeModel; protected function createModel()
public function __construct()
{ {
$this->model = new M713Model(); return new M713Model();
$this->codeModel = new CodeModel();
} }
protected function getCodeKeys(): array
public function lists(): string
{ {
$codes = $this->codeModel->getCodeLists(['STEP_VERIFICATION', 'VRFCREQ_WAY', 'CP_ID', 'ARTICLE_TYPE']); // 코드조회 return ['STEP_VERIFICATION', 'VRFCREQ_WAY', 'CP_ID', 'ARTICLE_TYPE'];
$sido = $this->model->getAreaList(); // 지역조회
$bonbu = $this->model->getBonbuList();
$team = $this->model->getTeamList();
$user = $this->model->getUserList();
$this->data['sido'] = $sido;
$this->data['bonbu'] = $bonbu;
$this->data['team'] = $team;
$this->data['user'] = $user;
$this->data['codes'] = $codes;
return view("pages/v2/m713/lists", $this->data);
} }
public function getResultList() protected function getViewName(): string
{ {
$start = (int) $this->request->getGet('start') ?: 0; return 'pages/v2/m713/lists';
$end = (int) $this->request->getGet('length') ?: 10; }
$data = [ protected function getSearchKeys(): array
'atcl_no' => $this->request->getGet('atcl_no'), // 매물번호 {
'stat_cd' => $this->request->getGet('stat_cd'), // 현재상태 return [
'realtor_nm' => $this->request->getGet('realtor_nm'), // 중개소 'atcl_no',
'charger_gbn' => $this->request->getGet('charger_gbn'), // 배정여부 'stat_cd',
'assign_yn' => $this->request->getGet('assign_yn'), // 배정여부2 'realtor_nm',
'receipt_sdate' => $this->request->getGet('receipt_sdate'), // 접수기간1 'charger_gbn',
'receipt_edate' => $this->request->getGet('receipt_edate'), // 접수기간2 'assign_yn',
'complete_sdate' => $this->request->getGet('complete_sdate'), // 완료기간1 'receipt_sdate',
'complete_edate' => $this->request->getGet('complete_edate'), // 완료기간2 'receipt_edate',
'srcSido' => $this->request->getGet('srcSido'), // 시도 'complete_sdate',
'srcGugun' => $this->request->getGet('srcGugun'), // 시군구 'complete_edate',
'srcDong' => $this->request->getGet('srcDong'), // 읍면동 'srcSido',
'bonbu' => $this->request->getGet('bonbu'), // 본부 'srcGugun',
'team' => $this->request->getGet('team'), // 팀 'srcDong',
'damdang' => $this->request->getGet('damdang'), // 담당 'bonbu',
'vrfcreq_way' => $this->request->getGet('vrfcreq_way'), // 검증방식1 'team',
'vrfc_type_sub' => $this->request->getGet('vrfc_type_sub'), // 검증방식2 'damdang',
'rcpt_cpid' => $this->request->getGet('rcpt_cpid'), // 매체사 'vrfcreq_way',
'rlet_type_cd' => $this->request->getGet('rlet_type_cd'), // 매물종류 'vrfc_type_sub',
'chk_spc_yn' => $this->request->getGet('chk_spc_yn'), // 면적확인 'rcpt_cpid',
'reference_file_url_yn' => $this->request->getGet('reference_file_url_yn'), // 참고용 'rlet_type_cd',
'corp_own' => $this->request->getGet('corp_own'), // 법인소유 'chk_spc_yn',
'reference_file_url_yn',
'corp_own',
]; ];
$totalCount = $this->model->getTotalCount($data);
$datas = $this->model->getResultList($start, $end, $data);
return $this->response->setJSON(body: [
'recordsTotal' => $totalCount,
'recordsFiltered' => $totalCount,
'data' => $datas,
]);
} }
// 엑셀 다운로드
public function excel()
{
try {
$data = [
'atcl_no' => $this->request->getGet('atcl_no'), // 매물번호
'stat_cd' => $this->request->getGet('stat_cd'), // 현재상태
'realtor_nm' => $this->request->getGet('realtor_nm'), // 중개소
'charger_gbn' => $this->request->getGet('charger_gbn'), // 배정여부
'assign_yn' => $this->request->getGet('assign_yn'), // 배정여부2
'receipt_sdate' => $this->request->getGet('receipt_sdate'), // 접수기간1
'receipt_edate' => $this->request->getGet('receipt_edate'), // 접수기간2
'complete_sdate' => $this->request->getGet('complete_sdate'), // 완료기간1
'complete_edate' => $this->request->getGet('complete_edate'), // 완료기간2
'srcSido' => $this->request->getGet('srcSido'), // 시도
'srcGugun' => $this->request->getGet('srcGugun'), // 시군구
'srcDong' => $this->request->getGet('srcDong'), // 읍면동
'bonbu' => $this->request->getGet('bonbu'), // 본부
'team' => $this->request->getGet('team'), // 팀
'damdang' => $this->request->getGet('damdang'), // 담당
'vrfcreq_way' => $this->request->getGet('vrfcreq_way'), // 검증방식1
'vrfc_type_sub' => $this->request->getGet('vrfc_type_sub'), // 검증방식2
'rcpt_cpid' => $this->request->getGet('rcpt_cpid'), // 매체사
'rlet_type_cd' => $this->request->getGet('rlet_type_cd'), // 매물종류
'chk_spc_yn' => $this->request->getGet('chk_spc_yn'), // 면적확인
'reference_file_url_yn' => $this->request->getGet('reference_file_url_yn'), // 참고용
'corp_own' => $this->request->getGet('corp_own'), // 법인소유
];
$datas = $this->model->getExcelList($data);
return $this->response->setJSON(body: [
'data' => $datas,
]);
} catch (\Exception $e) {
$e->getPrevious()->getTraceAsString();
}
}
// 상세화면
public function detail($id): string public function detail($id): string
{ {
$naver = new NaverApiClient(); $naver = new NaverApiClient();

View File

@@ -13,11 +13,32 @@ class AuthCheck implements FilterInterface
$session = session(); $session = session();
log_message('debug', 'URI PATH: ' . service('uri')->getPath()); log_message('debug', 'URI PATH: ' . service('uri')->getPath());
try {
// 세션 읽기 시도
$loggedIn = $session->get('logged_in');
// 로그인 체크 // 로그인 체크
if (!$session->get('logged_in')) { if (!$loggedIn) {
// 로그인 안 되어 있으면 로그인 페이지로 // 로그인 안 되어 있으면 로그인 페이지로
return redirect()->to('/login'); return redirect()->to('/login');
} }
} catch (\Exception $e) {
// 세션 오류 (Redis 다운 등) - 모든 예외 catch
log_message('error', '[SESSION ERROR in AuthCheck] ' . $e->getMessage());
// AJAX 요청인 경우 JSON 응답
if ($request->isAJAX()) {
return service('response')
->setStatusCode(503)
->setJSON([
'code' => '8',
'msg' => '세션 서비스 오류입니다. 페이지를 새로고침 해주세요. (Redis)'
]);
}
// 일반 요청인 경우 로그인 페이지로 리다이렉트 (에러 메시지 포함)
return redirect()->to('/login')->with('error', '세션 서비스 오류입니다. 시스템 관리자에게 문의해주세요.');
}
} }
public function after(RequestInterface $request, ResponseInterface $response, $arguments = null) public function after(RequestInterface $request, ResponseInterface $response, $arguments = null)

View File

@@ -7,40 +7,33 @@ if (!function_exists('limitHscpMarketPriceInfo')) {
* @param string $ptp_no 평형코드 * @param string $ptp_no 평형코드
* @param string $atcl_amt 가격 * @param string $atcl_amt 가격
*/ */
function limitHscpMarketPriceInfo($CI, $trade_type, $hscp_no, $ptp_no, $atcl_amt) function limitHscpMarketPriceInfo($trade_type, $hscp_no, $ptp_no, $atcl_amt)
{ {
$return = array(); $return = array();
if (!empty($hscp_no) && !empty($ptp_no)) { if (!empty($hscp_no) && !empty($ptp_no)) {
$hscpMarketPriceInfo = $CI->call_kiso_api->hscpMarketPriceInfo($hscp_no, $ptp_no); $naver = new \App\Libraries\NaverApiClient();
$hscpMarketPriceInfo = $naver->getComplexPriceByUnitType((int)$hscp_no, (int)$ptp_no);
if (isset($hscpMarketPriceInfo['error'])) { //결과값 확인 if (isset($hscpMarketPriceInfo['error']) && $hscpMarketPriceInfo['error']) { //결과값 확인
if ($hscpMarketPriceInfo['error']['code'] == 'VC027') { log_message('error', '네이버 시세 API 호출 실패: ' . json_encode($hscpMarketPriceInfo));
$return = array(); return array();
} else {
$return = $hscpMarketPriceInfo['error'];
}
} else { } else {
$limitH = 0; $limitH = 0;
$limitL = 0; $limitL = 0;
$sise = array();
$sise = $hscpMarketPriceInfo['data'];
// 상한가, 하한가 체크 ( 상한가 * 2, 하한가 * 0.7) 이내의 범위에 가격이 있어야 함. // 상한가, 하한가 체크 ( 상한가 * 2, 하한가 * 0.7) 이내의 범위에 가격이 있어야 함.
if ($trade_type == 'A1') { if ($trade_type == 'A1') {
// 매매 // 매매
if (isset($hscpMarketPriceInfo['result']['deal_uplmt_prc'])) { $limitH = $sise['dealCeilingPrice'] ?? 0;
$limitH = $hscpMarketPriceInfo['result']['deal_uplmt_prc']; $limitL = $sise['dealFloorPrice'] ?? 0;
}
if (isset($hscpMarketPriceInfo['result']['deal_lwlmt_prc'])) {
$limitL = $hscpMarketPriceInfo['result']['deal_lwlmt_prc'];
}
} elseif ($trade_type == 'B1') { } elseif ($trade_type == 'B1') {
// 전세 // 전세
if (isset($hscpMarketPriceInfo['result']['lease_uplmt_prc'])) { $limitH = $sise['leaseCeilingPrice'] ?? 0;
$limitH = $hscpMarketPriceInfo['result']['lease_uplmt_prc']; $limitL = $sise['leaseFloorPrice'] ?? 0;
}
if (isset($hscpMarketPriceInfo['result']['lease_lwlmt_prc'])) {
$limitL = $hscpMarketPriceInfo['result']['lease_lwlmt_prc'];
}
} }
if (!empty($limitH)) { if (!empty($limitH)) {
@@ -316,3 +309,146 @@ function getOwnerTypeCodeNo($code)
break; break;
} }
} }
/**
* 무엇이 변경되었는지 확인한다.
* $table(DB의 원래 데이터), data (변경된 내용) ==> array
*/
function what_is_changed($table, $data){
$return = '';
if (empty($table) || empty($data)){
log_message('warning', 'what_is_changed 함수에 빈 데이터 전달 | table: ' . json_encode($table) . ', data: ' . json_encode($data));
return $return;
}
foreach ( $data as $key => $value ) {
if (!array_key_exists($key, $table)) {
$table[$key] = '';
}
if (strcmp(trim((string) $table[$key]), trim((string) $value)) != 0){
switch ( $key ) {
case 'trade_type': // 거래구분
$return .= ', 거래구분 : ';
$return .= $table[$key] . ' => ' . $value . "\n";
break;
case 'rcpt_product_info2': // 매매, 전세, 월세보증금
$return .= ', 거래가 :';
$return .= $table[$key] . ' => ' . $value . "\n";
break;
case 'rcpt_product_info3': // 월세
$return .= ', 월세 :';
$return .= $table[$key] . ' => ' . $value . "\n";
break;
case 'rcpt_dtl_addr': // 상세주소
$return .= ', 상세주소 :';
$return .= $table[$key] . ' => ' . $value . "\n";
break;
case 'rcpt_li_addr': //리 주소
$return .= ', 리 주소 :';
$return .= $table[$key] . ' => ' . $value . "\n";
break;
case 'rcpt_jibun_addr': //지번 주소
$return .= ', 지번 주소 :';
$return .= $table[$key] . ' => ' . $value . "\n";
break;
case 'rcpt_etc_addr': //기타 주소
$return .= ', 기타 주소 :';
$return .= $table[$key] . ' => ' . $value . "\n";
break;
case 'rcpt_ref_addr': // 상세주소
$return .= ', 기타주소2 :';
$return .= $table[$key] . ' => ' . $value . "\n";
break;
case 'rcpt_ho': // 기타주소
$return .= ', 기타주소 :';
$return .= $table[$key] . ' => ' . $value . "\n";
break;
case 'rcpt_hscp_no': // 단지
$return .= ', 단지번호 :';
$return .= $table[$key] . ' => ' . $value . "\n";
break;
case 'rcpt_ptp_no': // 평형
$return .= ', 평형 :';
$return .= $table[$key] . ' => ' . $value . "\n";
break;
case 'rcpt_floor': // 층
$return .= ', 층 :';
$return .= $table[$key] . ' => ' . $value . "\n";
break;
case 'exp_spc_yn': // 층
$return .= ', 면적확인여부 :';
$return .= $table[$key] . ' => ' . $value . "\n";
break;
case 'excls_spc1': // 층
$return .= ', 전용면적 첫번째 값 :';
$return .= $table[$key] . ' => ' . $value . "\n";
break;
case 'excls_spc2': // 층
$return .= ', 전용면적 두번째 값 :';
$return .= $table[$key] . ' => ' . $value . "\n";
break;
case 'room_cnt': // 층
$return .= ', 방개수 :';
$return .= $table[$key] . ' => ' . $value . "\n";
break;
case 'sply_spc': // 층
$return .= ', 공급면적 값 :';
$return .= $table[$key] . ' => ' . $value . "\n";
break;
case 'share_spc': // 층
$return .= ', 공용면적 값 :';
$return .= $table[$key] . ' => ' . $value . "\n";
break;
case 'tot_spc': // 층
$return .= ', 연면적 :';
$return .= $table[$key] . ' => ' . $value . "\n";
break;
case 'tot_spc1': // 층
$return .= ', 연면적 첫번째 값 :';
$return .= $table[$key] . ' => ' . $value . "\n";
break;
case 'tot_spc2': // 층
$return .= ', 연면적 두번째 값 :';
$return .= $table[$key] . ' => ' . $value . "\n";
break;
case 'grnd_spc1': // 층
$return .= ', 대지면적 첫번째 값 :';
$return .= $table[$key] . ' => ' . $value . "\n";
break;
case 'grnd_spc2': // 층
$return .= ', 대지면적 두번째 값 :';
$return .= $table[$key] . ' => ' . $value . "\n";
break;
case 'grnd_spc3': // 층
$return .= ', 대지면적 세번째 값 :';
$return .= $table[$key] . ' => ' . $value . "\n";
break;
case 'grnd_spc4': // 층
$return .= ', 대지면적 네번째 값 :';
$return .= $table[$key] . ' => ' . $value . "\n";
break;
case 'grnd_spc5': // 층
$return .= ', 대지면적 다섯번째 값 :';
$return .= $table[$key] . ' => ' . $value . "\n";
break;
case 'spc_stat': // 층
$return .= ', 면적구분 :';
$return .= $table[$key] . ' => ' . $value . "\n";
break;
case 'request_msg': // 층
$return .= ', 메모변경 :';
$return .= $table[$key] . ' => ' . $value . "\n";
break;
// case 'rcpt_x': // 좌표 x 12x.xxxxx (경도: longitude)
// $return .= '거래구분 :';
// break;
// case 'rcpt_y': // 좌표 y 3x.xxxx (위도: latitude)
// $return .= '거래구분 :';
// break;
}
}
}
return trim(substr($return, 1));
}

View File

@@ -6,7 +6,7 @@ if (! function_exists('write_custom_log')) {
*/ */
function write_custom_log($message, $level = 'INFO', $type = 'service') function write_custom_log($message, $level = 'INFO', $type = 'service')
{ {
$logDir = WRITEPATH . 'logs/worker'; $logDir = WRITEPATH . 'logs';
if (!is_dir($logDir)) { if (!is_dir($logDir)) {
@mkdir($logDir, 0777, true); @mkdir($logDir, 0777, true);
} }
@@ -31,8 +31,8 @@ if (! function_exists('write_custom_log')) {
} }
// ---------------------------- // ----------------------------
$suffix = ($type === 'failed') ? '_failed' : ''; $suffix = ($type === 'failed') ? '-failed' : '';
$logFile = $logDir . '/' . date('Y-m-d') . $suffix . '.log'; $logFile = $logDir . '/log-' . date('Y-m-d') . $suffix . '.log';
$timestamp = date('Y-m-d H:i:s'); $timestamp = date('Y-m-d H:i:s');
$singleLine = str_replace(["\r", "\n", "\t"], " ", $message); $singleLine = str_replace(["\r", "\n", "\t"], " ", $message);

View File

@@ -0,0 +1,35 @@
<?php
if (!function_exists('write_query_log')) {
/**
* SQL 쿼리 로그를 writable/logs/sql-query-{date}.log에 기록
*
* @param string $message 로그 메시지
* @param string $sql SQL 쿼리 (선택)
*/
function write_query_log(string $message, string $sql = '')
{
$logDir = WRITEPATH . 'logs';
$logFile = $logDir . '/sql-query-' . date('Y-m-d') . '.log';
$timestamp = date('Y-m-d H:i:s');
$logMessage = "[{$timestamp}] {$message}";
if (!empty($sql)) {
$logMessage .= "\nSQL: {$sql}";
}
$logMessage .= "\n" . str_repeat('-', 80) . "\n";
// 로그 디렉토리 확인 및 생성
if (!is_dir($logDir)) {
mkdir($logDir, 0755, true);
}
// 파일에 쓰기
file_put_contents($logFile, $logMessage, FILE_APPEND | LOCK_EX);
// log_message도 호출 (작동하면 좋고, 안되도 파일에는 기록됨)
log_message('error', $message);
}
}

View File

@@ -0,0 +1,82 @@
<?php
if (!function_exists('get_redis_connection')) {
/**
* Redis 연결을 반환하는 헬퍼 함수
* .env 환경변수에서 설정을 읽어 Redis에 연결합니다.
*
* @param string $type 'worker' 또는 'session' (기본값: 'worker')
* @return Redis|false Redis 객체 또는 연결 실패 시 false
* @throws RedisException
*/
function get_redis_connection(string $type = 'worker')
{
$redis = new \Redis();
try {
// 타입에 따라 다른 환경변수 사용
if ($type === 'session') {
$host = env('SESSION_REDIS_HOST', '127.0.0.1');
$port = env('SESSION_REDIS_PORT', '6379');
$database = env('SESSION_REDIS_DATABASE', '0');
$password = env('SESSION_REDIS_PASSWORD', '');
} else {
// worker, default
$host = env('REDIS_HOST', '127.0.0.1');
$port = env('REDIS_PORT', '6379');
$database = env('REDIS_DATABASE', '9');
$password = env('REDIS_PASSWORD', '');
}
// Redis 연결 (타임아웃: 0.5초)
$timeout = 0.5;
$success = $redis->connect($host, (int)$port, $timeout);
if (!$success) {
log_message('error', "Redis connection failed: {$host}:{$port}");
return false;
}
// 비밀번호 인증 (있는 경우)
if (!empty($password)) {
$redis->auth($password);
}
// 데이터베이스 선택
$redis->select((int)$database);
return $redis;
} catch (\RedisException $e) {
log_message('error', "Redis connection error: " . $e->getMessage());
return false;
}
}
}
if (!function_exists('get_redis_config')) {
/**
* Redis 설정 정보를 배열로 반환
*
* @param string $type 'worker' 또는 'session'
* @return array Redis 설정 배열
*/
function get_redis_config(string $type = 'worker'): array
{
if ($type === 'session') {
return [
'host' => env('SESSION_REDIS_HOST', '127.0.0.1'),
'port' => env('SESSION_REDIS_PORT', '6379'),
'database' => env('SESSION_REDIS_DATABASE', '0'),
'password' => env('SESSION_REDIS_PASSWORD', ''),
];
}
return [
'host' => env('REDIS_HOST', '127.0.0.1'),
'port' => env('REDIS_PORT', '6379'),
'database' => env('REDIS_DATABASE', '9'),
'password' => env('REDIS_PASSWORD', ''),
];
}
}

View File

@@ -49,7 +49,6 @@ class Common
$wmTextAlpha = 0.5; // 워터마크 텍스트 투명도 $wmTextAlpha = 0.5; // 워터마크 텍스트 투명도
try { try {
$img = new \Imagick(); $img = new \Imagick();
if (is_string($imagePath) && is_file($imagePath)) { if (is_string($imagePath) && is_file($imagePath)) {
$img->readImage($imagePath); $img->readImage($imagePath);
@@ -96,19 +95,23 @@ class Common
} }
} }
if (empty($wmImagePath)) if (empty($wmImagePath)) {
return; return;
}
// 워터마크 이미지 경로 처리
if (substr($wmImagePath, 0, 1) == '/') { if (substr($wmImagePath, 0, 1) == '/') {
$wmImagePath = substr($wmImagePath, 1); $wmImagePath = substr($wmImagePath, 1);
} }
// ROOTPATH와 결합하여 절대 경로 생성 (img 폴더는 프로젝트 루트에 있음)
$fullWmPath = ROOTPATH . $wmImagePath;
// echo FCPATH.$wmImagePath; if (!is_file($fullWmPath)) {
return;
}
$wm = new \Imagick($fullWmPath);
$wm = new \Imagick($wmImagePath);
$hWm = $wm->getImageHeight(); $hWm = $wm->getImageHeight();
$wWm = $wm->getImageWidth(); $wWm = $wm->getImageWidth();
@@ -119,12 +122,16 @@ class Common
$img->compositeImage($wm, \Imagick::COMPOSITE_OVER, $wmImgLeft, $wmImgTop); $img->compositeImage($wm, \Imagick::COMPOSITE_OVER, $wmImgLeft, $wmImgTop);
$wm->destroy(); $wm->destroy();
// 워터마크 텍스트가 있는 경우에만 텍스트 그리기
if (!empty($wmText)) {
$draw = new \ImagickDraw(); $draw = new \ImagickDraw();
$basePath = defined('BASEPATH') ? BASEPATH : (defined('ROOTPATH') ? ROOTPATH . 'system/' : ''); // 폰트 경로: img/watermark/fonts/
$wmFont = $basePath . 'fonts/' . $wmFont; if (!empty($wmFont)) {
if (is_file($wmFont)) { $fontPath = ROOTPATH . 'img/watermark/fonts/' . $wmFont;
$draw->setFont($wmFont); if (is_file($fontPath)) {
$draw->setFont($fontPath);
}
} }
$draw->setFontSize($wmFontSize); $draw->setFontSize($wmFontSize);
@@ -141,6 +148,7 @@ class Common
$img->drawImage($draw); $img->drawImage($draw);
$draw->destroy(); $draw->destroy();
}
// $img->writeImage(); // $img->writeImage();
@@ -149,14 +157,9 @@ class Common
if (empty($key)) { if (empty($key)) {
throw new \RuntimeException('Empty upload key'); throw new \RuntimeException('Empty upload key');
} }
$ok = $uploader->upload_object_storage_imagick2($key, $watermark_img); $ok = $uploader->upload_object_storage_imagick2($key, $watermark_img);
if ($ok) { if (!$ok) {
log_message('info', '[watermarking] upload success key={key} size={size} cpid={cpid}', [
'key' => $key,
'size' => strlen($watermark_img),
'cpid' => $cpid,
]);
} else {
log_message('error', '[watermarking] upload failed key={key} cpid={cpid}', [ log_message('error', '[watermarking] upload failed key={key} cpid={cpid}', [
'key' => $key, 'key' => $key,
'cpid' => $cpid, 'cpid' => $cpid,
@@ -166,7 +169,11 @@ class Common
// $object_upload = $this->upload->upload_object_storage($imagePath , $imagePath , 'data'); // $object_upload = $this->upload->upload_object_storage($imagePath , $imagePath , 'data');
} catch (\Throwable $e) { } catch (\Throwable $e) {
log_message('error', '[watermarking] ' . $e->getMessage()); log_message('error', '[watermarking] Exception: {message} at {file}:{line}', [
'message' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
]);
} }
} }

View File

@@ -54,7 +54,7 @@ class MyUpload
} }
/** /**
* 파일 업로드 요청 * 파일 업로드 요청 (Object Storage 전용)
* 추가일 2025.12.24 * 추가일 2025.12.24
* 작성자 - yangsh * 작성자 - yangsh
*/ */
@@ -73,39 +73,40 @@ class MyUpload
$newName = $file->getRandomName(); $newName = $file->getRandomName();
// ✅ PHP 임시 업로드 파일 경로 (writable로 move() 필요 없음) // ✅ PHP 임시 업로드 파일 경로
$tmpFile = $file->getTempName(); $tmpFile = $file->getTempName();
if (!is_file($tmpFile)) { if (!is_file($tmpFile)) {
$this->set_error('upload_temp_file_missing'); $this->set_error('upload_temp_file_missing');
log_message('error', 'do_upload2 temp file missing: ' . $tmpFile); log_message('error', '[MyUpload] Temp file missing: ' . $tmpFile);
return false; return false;
} }
// ✅ 클라우드에 올라갈 "Key"를 직접 만든다 (로컬 경로 절대 넣지 말기) // ✅ 클라우드에 올라갈 "Key"
// 예시: upload/tmp/랜덤파일명 또는 upload/apt_file/{rcpt_no}/...
$objectKey = $filePath . $newName; $objectKey = $filePath . $newName;
// 클라우드 업로드 (필수)
$up = $this->upload_object_storage($objectKey, $tmpFile, 'file'); $up = $this->upload_object_storage($objectKey, $tmpFile, 'file');
if ($up === false) { if ($up === false) {
$this->set_error('upload_destination_cloud_error'); $this->set_error('cloud_upload_failed');
log_message('error', '[MyUpload] Cloud upload failed: ' . $objectKey);
@unlink($tmpFile);
return false; return false;
} }
// (선택) tmp 파일 삭제 // tmp 파일 삭제
@unlink($tmpFile); @unlink($tmpFile);
$this->s3_data = [ $this->s3_data = [
'object_key' => $objectKey, 'object_key' => $objectKey,
'object_storage_url' => $up['object_storage_url'] ?? null, 'object_storage_url' => $up['object_storage_url'] ?? null,
'origin_name' => $file->getClientName(), 'origin_name' => $file->getClientName(),
'file_name' => basename($objectKey), // xxxx.jpg 'file_name' => basename($objectKey),
'base_name' => pathinfo($objectKey, PATHINFO_FILENAME), // xxxx 'base_name' => pathinfo($objectKey, PATHINFO_FILENAME),
'ext' => pathinfo($objectKey, PATHINFO_EXTENSION), // jpg 'ext' => pathinfo($objectKey, PATHINFO_EXTENSION),
]; ];
log_message('info', '[MyUpload] Cloud upload success: ' . $objectKey);
log_message('debug', 's3_data=' . json_encode($this->s3_data ?? null, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
return $this->s3_data; return $this->s3_data;
} }
@@ -386,6 +387,7 @@ class MyUpload
'Key' => ltrim($object_storage_upload_path, '/'), 'Key' => ltrim($object_storage_upload_path, '/'),
'Body' => $blobData, 'Body' => $blobData,
'ACL' => 'public-read', 'ACL' => 'public-read',
'ContentType' => 'image/jpeg',
]); ]);

View File

@@ -41,13 +41,36 @@ class NaverApiClient
* object $space 면적정보[비공동] (supplySpace,exclusiveSpace,totalSpace,groundSpace,buildingSpace) * object $space 면적정보[비공동] (supplySpace,exclusiveSpace,totalSpace,groundSpace,buildingSpace)
* object $facilities 비공동시설정보 (roomCount) * object $facilities 비공동시설정보 (roomCount)
*/ */
/** 현장확인 수정시 */
public function updateArticleInfo(string $articleNumber, array $updateData, string $charger = 'admin'): ?array public function updateArticleInfo(string $articleNumber, array $updateData, string $charger = 'admin'): ?array
{
$this->charger = $charger;
$url = "{$this->baseUrl}/kiso/center/verification-site/{$articleNumber}?charger={$this->charger}";
log_message('info', "[updateArticleInfo] 호출됨 - URL: {$url}, 데이터: " . json_encode($updateData, JSON_UNESCAPED_UNICODE));
$result = $this->request('PUT', $url, $updateData);
log_message('info', "[updateArticleInfo] 결과 - " . ($result === null ? 'NULL' : json_encode($result, JSON_UNESCAPED_UNICODE)));
return $result;
}
/** 일반매물 수정시 */
public function v2updateArticleInfo(string $articleNumber, array $updateData, string $charger = 'admin'): ?array
{ {
$this->charger = $charger; $this->charger = $charger;
$url = "{$this->baseUrl}/kiso/center/verification-article/{$articleNumber}?charger={$this->charger}"; $url = "{$this->baseUrl}/kiso/center/verification-article/{$articleNumber}?charger={$this->charger}";
return $this->request('PUT', $url, $updateData); log_message('info', "[v2updateArticleInfo] 호출됨 - URL: {$url}, 데이터: " . json_encode($updateData, JSON_UNESCAPED_UNICODE));
$result = $this->request('PUT', $url, $updateData);
log_message('info', "[v2updateArticleInfo] 결과 - " . ($result === null ? 'NULL' : json_encode($result, JSON_UNESCAPED_UNICODE)));
return $result;
} }
/** /**
* [POST] 4.매물검증 결과관리 * [POST] 4.매물검증 결과관리
* API:POST /kiso/center/verification-article/{매물번호}/report?charger={담당자명} * API:POST /kiso/center/verification-article/{매물번호}/report?charger={담당자명}
@@ -108,9 +131,132 @@ class NaverApiClient
$this->charger = $charger; $this->charger = $charger;
$url = "{$this->baseUrl}/kiso/center/verification-article/price/{$articleNumber}?charger={$this->charger}"; $url = "{$this->baseUrl}/kiso/center/verification-article/price/{$articleNumber}?charger={$this->charger}";
return $this->request('POST', $url, $priceData); return $this->request('PATCH', $url, $priceData);
} }
/**
* 현장확인 슬롯 동기화
* API (가) - /kiso/center/site-slot
* 신규/변경 일자별 데이터 전송
* @syncData array
* @param string $baseDate 기준일자(ISO 8601 형식: YYYY-MM-DD 예: 2024-01-31)
* @param string $legalDivisionNumber 구역번호 "1111000000"
* @param object slots 오전/오후/ 슬롯 정보
[
{
"baseDate": "2025-04-30",
"legalDivisionNumber": "1111000000",
"slots": {
"am": { "max": 10, "reserved": 0 },
"pm": { "max": 100, "reserved": 0 }
}
}
]
*/
public function syncSiteSlot($syncData){
$url = $this->baseUrl . "/kiso/center/verification-site/slots";
return $this->request('POST', $url, $syncData);
}
/**
* 현장확인 매물검증 결과 관리
* API: POST /kiso/center/verification-site/{매물번호}/report?charger={담당자명}
* @param string $type 현장확인 S11: 예약, S21: 방문, S31: 검수
* @param string $code 결과코드 10000: 성공, 20000: 실패, 30000: 지연, 20121: 방문전취소, 20122:방문후취소, 20123:촬영후취소
* @param string $message 내용
* @param string $reserveDate 예약 년월일 2005-07-31
*/
public function verificationSiteReport(string $articleNumber, array $reportData, string $charger = 'admin'): ?array
{
$this->charger = $charger;
$url = "{$this->baseUrl}/kiso/center/verification-site/{$articleNumber}/report?charger={$this->charger}";
return $this->request('POST', $url, $reportData);
}
/**
* 특정 단지/평형 시세조회()
* @param Long hscpNo 단지번호
* @param int ptpNo 평형번호
* API: GET /confirms/hscpMarketPriceInfo.nhn?hscpNo={단지번호}&ptpNo={평형번호}
*/
// public function hscpMarketPriceInfo($hscpNo, $ptpNo){
// $url = '/confirms/hscpMarketPriceInfo.nhn';
// $url = $this->commonModel->getCompanyInfo(3);
// $url = $url['api_server'] . $url;
// $data = [
// 'hscpNo' => $hscpNo,
// 'ptpNo' => $ptpNo
// ];
// return $this->request('GET', $url, $data);
// }
public function getComplexPriceByUnitType(int $hscpNo, int $ptpNo): ?array
{
$url = $this->baseUrl . "/kiso/marketprice/complex/{$hscpNo}/pyeong-type/{$ptpNo}";
return $this->request('GET', $url);
}
/**
* 법정동 기준 단지 목록 조회
* API: GET /kiso/complex/legal-division/{법정동코드}
* legalDivisionNumber : 법정동 코드
*/
public function getComplexList($legalDivisionNumber, $realEstateType = null)
{
$url = $this->baseUrl . "/kiso/complex/legal-division/{$legalDivisionNumber}";
if ($realEstateType !== null) {
$url .= "?realEstateType={$realEstateType}";
}
return $this->request('GET', $url);
}
/**
* 단지평형목록 조회(아파트/오피스텔)
*/
public function getPyeongTypeList($complexNumber){
$url = $this->baseUrl . "/kiso/complex/{$complexNumber}/pyeongs";
return $this->request('GET', $url);
}
/**
* 빌라 단지 평형 목록 조회
*/
public function getVillaPyeongTypeList($complexNumber){
$url = $this->baseUrl . "/kiso/complex/villa/{$complexNumber}/pyeongs";
return $this->request('GET', $url);
}
/**
* 단지 상세 조회
*/
public function getComplexDetail($complexNumber){
$url = $this->baseUrl . "/kiso/complex/{$complexNumber}";
return $this->request('GET', $url);
}
/**
* 빌라 단지 상세 조회
*/
public function getVillaComplexDetail($complexNumber){
$url = $this->baseUrl . "/kiso/complex/villa/{$complexNumber}";
return $this->request('GET', $url);
}
/**
* 빌라 단지 동호수 조회
*/
public function getVillaBuildingList($complexNumber){
$url = $this->baseUrl . "/kiso/complex/villa/{$complexNumber}/buildings";
return $this->request('GET', $url);
}
/**
* 현장확인 동기화 결과 전송
* API: GET /site/submitSyncResult.nhn?reserveNoList={예약번호리스트}
* @param string reserveNoList 예약번호 리스트 (쉼표로 구분된 문자열, 예: "12345,67890,54321")
*/
public function submitSyncResult(string $reserveNoList): ?array public function submitSyncResult(string $reserveNoList): ?array
{ {
$url = "{$this->baseUrl}/site/submitSyncResult.nhn"; $url = "{$this->baseUrl}/site/submitSyncResult.nhn";
@@ -434,16 +580,17 @@ class NaverApiClient
return $this->request('POST', $url, $postData); return $this->request('POST', $url, $postData);
} }
/** /**
* CURL 공통 실행 함수 * CURL 공통 실행 함수
*/ */
private function request(string $method, string $url, ?array $data = null): ?array private function request(string $method, string $url, ?array $data = null): ?array
{ {
/** // 요청 데이터 로깅
* curl --location 'https://test-b2b.land.naver.com/kiso/center/verification-article/2500000001?charger=admin' \ if ($data) {
--header 'X-Naver-Client-Id: yqBbvQZ123_hjH3b3Df9' \ log_message('info', "[Naver API $method REQUEST] URL: $url | Data: " . json_encode($data, JSON_UNESCAPED_UNICODE));
*/ } else {
log_message('info', "[Naver API $method REQUEST] URL: $url");
}
$ch = curl_init(); $ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_URL, $url);
@@ -491,14 +638,34 @@ class NaverApiClient
$response = curl_exec($ch); $response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlError = curl_error($ch);
$curlErrno = curl_errno($ch);
curl_close($ch); curl_close($ch);
// CURL 오류 체크
if ($curlErrno !== 0) {
log_message('error', "[Naver API $method CURL ERROR] URL: $url | Error ($curlErrno): $curlError");
return [
'error' => true,
'error_type' => 'CURL_ERROR',
'curl_errno' => $curlErrno,
'curl_error' => $curlError,
'url' => $url
];
}
// 결과 로그 기록 (성공/실패 모두 기록하여 추적 가능하게 함) // 결과 로그 기록 (성공/실패 모두 기록하여 추적 가능하게 함)
if ($httpCode === 200 || $httpCode === 202) { if ($httpCode === 200 || $httpCode === 202) {
log_message('info', "[Naver API $method SUCCESS] URL: $url | Response: $response"); log_message('info', "[Naver API $method SUCCESS] URL: $url | Code: $httpCode | Response: $response");
} else { } else {
log_message('error', "[Naver API $method FAIL] URL: $url | Code: $httpCode | Response: $response"); log_message('error', "[Naver API $method FAIL] URL: $url | Code: $httpCode | Response: $response");
return null; return [
'error' => true,
'error_type' => 'HTTP_ERROR',
'http_code' => $httpCode,
'response' => $response,
'url' => $url
];
} }
return json_decode($response, true); return json_decode($response, true);

View File

@@ -0,0 +1,47 @@
<?php
namespace App\Models\Entities;
use CodeIgniter\Model;
class ChangedHistoryModel extends Model
{
protected $table = 'changed_history';
protected $primaryKey = 'seq';
protected $useAutoIncrement = true;
protected $returnType = 'array';
protected $protectFields = true;
protected $allowedFields = [
'rcpt_sq',
'rcpt_stat',
'changed_type',
'changed_id',
'changed_tm',
'remark',
];
/**
* 정보변경 이력 1건 저장
*/
public function addHistory(
int $rcptSq,
?string $rcptStat,
?string $changedType,
?string $changedId,
?string $remark,
?string $changedTm = null
): bool {
$data = [
'rcpt_sq' => $rcptSq,
'rcpt_stat' => $rcptStat,
'changed_type' => $changedType,
'changed_id' => $changedId,
'changed_tm' => $changedTm ?? date('Y-m-d H:i:s'),
'remark' => $remark,
];
return $this->insert($data) !== false;
}
}

View File

@@ -13,7 +13,10 @@ class ReceiptModel extends Model
protected $allowedFields = [ protected $allowedFields = [
'comp_sq', 'rcpt_rating', 'rcpt_key', 'rcpt_atclno', 'rcpt_type', 'comp_sq', 'rcpt_rating', 'rcpt_key', 'rcpt_atclno', 'rcpt_type',
'rcpt_product', 'rcpt_product_nm', 'rcpt_product_info1', 'rcpt_product_info2', 'rcpt_product', 'rcpt_product_nm', 'rcpt_product_info1', 'rcpt_product_info2',
'rcpt_product_info3', 'rcpt_office', 'rcpt_agent', 'rcpt_sido', 'rcpt_hscp_nm', 'rcpt_product_info3', 'rcpt_product_info4', 'rcpt_product_info5', 'rcpt_product_info6',
'trade_type', 'rcpt_ptp_no', 'rcpt_hscp_no',
'dealAmount', 'warrantyAmount', 'leaseAmount', 'preSaleAmount', 'premiumAmount', 'preSaleOptionAmount',
'rcpt_office', 'rcpt_agent', 'rcpt_sido', 'rcpt_hscp_nm',
'rcpt_dtl_addr', 'rcpt_etc_addr', 'rcpt_floor', 'rcpt_floor2', 'rcpt_tm', 'rcpt_dtl_addr', 'rcpt_etc_addr', 'rcpt_floor', 'rcpt_floor2', 'rcpt_tm',
'rcpt_stat', 'rcpt_x', 'rcpt_y', 'agent_nm', 'agent_head_tel', 'rsrv_date', 'rcpt_stat', 'rcpt_x', 'rcpt_y', 'agent_nm', 'agent_head_tel', 'rsrv_date',
'insert_tm', 'rcpt_cpid', 'room_cnt', 'isSiteVRVerification' 'insert_tm', 'rcpt_cpid', 'room_cnt', 'isSiteVRVerification'
@@ -104,4 +107,5 @@ class ReceiptModel extends Model
'total' => $totalCount 'total' => $totalCount
]; ];
} }
} }

View File

@@ -0,0 +1,36 @@
<?php
namespace App\Models\Entities;
use CodeIgniter\Model;
class RegionModel extends Model
{
protected $table = 'region_codes';
protected $primaryKey = 'region_cd';
protected $returnType = 'array';
protected $useSoftDeletes = false;
protected $useTimestamps = false;
protected $allowedFields = [
'region_cd', 'region_nm', 'use_yn', 'dept_sq', 'usr_sq'
];
/**
* 지역 코드로 담당자 정보 조회
*
* @param string $regionCd 지역 코드 (sectorNumber)
* @return array|null ['dept_sq' => 26, 'usr_sq' => 1] 또는 null
*/
public function getChargeByRegionCd($regionCd)
{
if (empty($regionCd)) {
return null;
}
return $this->select('dept_sq, usr_sq')
->where('region_cd', $regionCd)
->where('use_yn', 'Y')
->first();
}
}

View File

@@ -0,0 +1,154 @@
<?php
namespace App\Models\Entities;
use CodeIgniter\Model;
class WatermarkModel extends Model
{
protected $table = 'watermark';
protected $primaryKey = ['cpid', 'wm_type']; // 복합키
protected $useAutoIncrement = false;
protected $returnType = 'array';
protected $useSoftDeletes = false;
protected $allowedFields = [
'cpid',
'wm_type',
'wm_img_path',
'wm_img_height',
'wm_img_width',
'wm_position',
'wm_img_opacity',
'wm_space',
'text_font',
'text_color',
'text_opacity',
'text_size',
'text_pixel',
'img_width_min',
'img_width_max'
];
protected $useTimestamps = false;
protected $validationRules = [
'cpid' => 'required|max_length[20]',
'wm_type' => 'required|integer',
'wm_img_path' => 'required|max_length[300]',
'wm_img_height' => 'required|integer',
'wm_img_width' => 'required|integer',
'wm_position' => 'required|max_length[2]',
'wm_img_opacity' => 'required|integer',
'wm_space' => 'required|integer',
'img_width_min' => 'required|integer',
'img_width_max' => 'required|integer'
];
protected $validationMessages = [];
protected $skipValidation = false;
/**
* 워터마크 정보 조회 (복합키)
*
* @param string $cpid
* @param int $wm_type
* @return array|null
*/
public function getWatermark($cpid, $wm_type)
{
return $this->where('cpid', $cpid)
->where('wm_type', $wm_type)
->first();
}
/**
* cpid로 워터마크 목록 조회
*
* @param string $cpid
* @return array
*/
public function getWatermarksByCpid($cpid)
{
return $this->where('cpid', $cpid)->findAll();
}
/**
* 워터마크 타입별 조회
*
* @param int $wm_type
* @return array
*/
public function getWatermarksByType($wm_type)
{
return $this->where('wm_type', $wm_type)->findAll();
}
/**
* 워터마크 정보 저장/업데이트
*
* @param array $data
* @return bool
*/
public function saveWatermark($data)
{
if (!isset($data['cpid']) || !isset($data['wm_type'])) {
return false;
}
// 복합키로 기존 데이터 확인
$existing = $this->getWatermark($data['cpid'], $data['wm_type']);
if ($existing) {
// 업데이트
return $this->where('cpid', $data['cpid'])
->where('wm_type', $data['wm_type'])
->set($data)
->update();
} else {
// 삽입
return $this->insert($data);
}
}
/**
* 워터마크 삭제 (복합키)
*
* @param string $cpid
* @param int $wm_type
* @return bool
*/
public function deleteWatermark($cpid, $wm_type)
{
return $this->where('cpid', $cpid)
->where('wm_type', $wm_type)
->delete();
}
/**
* cpid로 모든 워터마크 삭제
*
* @param string $cpid
* @return bool
*/
public function deleteAllByCpid($cpid)
{
return $this->where('cpid', $cpid)->delete();
}
/**
* 이미지 크기 범위로 워터마크 조회
*
* @param string $cpid
* @param int $img_width
* @return array|null
*/
public function getWatermarkByImageSize($cpid, $img_width)
{
return $this->where('cpid', $cpid)
->where('img_width_min <=', $img_width)
->where('img_width_max >=', $img_width)
->findAll();
}
}

View File

@@ -212,75 +212,88 @@ class ProcessibleModel extends Model
// 지역별 수량 저장 // 지역별 수량 저장
public function saveArea($data) public function saveArea($data)
{ {
$this->db->transStart();
$usr_sq = session('usr_sq'); $usr_sq = session('usr_sq');
$now = date('Y-m-d H:i:s');
$sql = "INSERT INTO service_count $insertData = [
(sc_date, region_cd, am_cnt, pm_cnt, day_cnt, insert_usr, insert_tm, update_usr, update_tm) 'sc_date' => $data['sc_date'],
VALUES 'region_cd' => $data['region_cd'],
(?, ?, ?, ?, ?, ?, NOW(), ?, NOW()) 'am_cnt' => $data['am_cnt'],
ON DUPLICATE KEY UPDATE am_cnt=values(am_cnt), pm_cnt=values(pm_cnt), day_cnt=values(day_cnt), update_usr=values(update_usr) 'pm_cnt' => $data['pm_cnt'],
"; 'day_cnt' => (int)$data['am_cnt'] + (int)$data['pm_cnt'],
'insert_usr' => $usr_sq,
$datas = [ 'insert_tm' => $now,
$data['sc_date'], 'update_usr' => $usr_sq,
$data['region_cd'], 'update_tm' => $now
$data['am_cnt'],
$data['pm_cnt'],
((int) $data['am_cnt'] + (int) $data['pm_cnt']),
$usr_sq,
$usr_sq
]; ];
if ($this->db->query($sql, $datas) === false) { // CI4 Query Builder upsert (INSERT ... ON DUPLICATE KEY UPDATE)
return [ $result = $this->db->table('service_count')->upsert($insertData);
'success' => false,
'msg' => '저장실패',
];
}
$this->db->transComplete();
return [ return [
'success' => true, 'success' => $result !== false,
'msg' => $result !== false ? '성공' : '저장실패'
]; ];
} }
public function saveCount($data) public function saveCount($data)
{ {
$this->db->transStart();
$usr_sq = session('usr_sq'); $usr_sq = session('usr_sq');
$now = date('Y-m-d H:i:s');
$sql = "INSERT INTO service_count $insertData = [
(sc_date, region_cd, am_cnt, pm_cnt, day_cnt, insert_usr, insert_tm, update_usr, update_tm) 'sc_date' => '0000-00-00',
VALUES 'region_cd' => $data['region_cd'],
(?, ?, ?, ?, ?, ?, NOW(), ?, NOW()) 'am_cnt' => $data['am_cnt'],
ON DUPLICATE KEY UPDATE am_cnt=values(am_cnt), pm_cnt=values(pm_cnt), day_cnt=values(day_cnt), update_usr=values(update_usr) 'pm_cnt' => $data['pm_cnt'],
"; 'day_cnt' => (int)$data['am_cnt'] + (int)$data['pm_cnt'],
'insert_usr' => $usr_sq,
$datas = [ 'insert_tm' => $now,
'0000-00-00', 'update_usr' => $usr_sq,
$data['region_cd'], 'update_tm' => $now
$data['am_cnt'],
$data['pm_cnt'],
((int) $data['am_cnt'] + (int) $data['pm_cnt']),
$usr_sq,
$usr_sq
]; ];
if ($this->db->query($sql, $datas) === false) { // CI4 Query Builder upsert
$result = $this->db->table('service_count')->upsert($insertData);
// 실행된 쿼리 로그 출력
$lastQuery = $this->db->getLastQuery();
log_message('info', '[ProcessibleModel::saveCount] Query: ' . $lastQuery);
log_message('info', '[ProcessibleModel::saveCount] Affected Rows: ' . $this->db->affectedRows());
if ($result === false) {
$error = $this->db->error();
log_message('error', '[ProcessibleModel::saveCount] Error: ' . json_encode($error));
return [ return [
'success' => false, 'success' => false,
'msg' => '저장실패', 'msg' => '저장실패: ' . ($error['message'] ?? '알 수 없는 오류'),
'query' => (string) $lastQuery,
'error' => $error
]; ];
} }
$this->db->transComplete();
return [ return [
'success' => true, 'success' => true,
'query' => (string) $lastQuery,
'affected_rows' => $this->db->affectedRows()
]; ];
} }
/**
* 슬롯 동기화
*
*/
public function getSyncSlotData($baseDate, $region_cd)
{
$sql = "SELECT sc_date, region_cd, am_cnt, pm_cnt, day_cnt FROM service_count WHERE sc_date = ? AND region_cd IN ?";
$datas = [
$baseDate,
$region_cd
];
$query = $this->db->query($sql, $datas);
return $query->getResultArray();
}
} }

File diff suppressed because it is too large Load Diff

View File

@@ -8,64 +8,85 @@ class CodeModel extends Model
/** /**
* 코드목록 읽어오기(Y만) * 코드목록 읽어오기(Y만)
*/ */
public function getCodeList($category) public function getCodeList(string $category): array
{
$sql = "SELECT category, category_nm, cd, cd_nm FROM codes" .
" WHERE category = ?" .
" AND use_yn = 'Y'" .
" ORDER BY view_odr";
$data = [$category];
$query = $this->db->query($sql, $data);
return $query->getResultArray();
}
public function getCodeLists($data): array
{ {
return $this->db->table('codes') return $this->db->table('codes')
->select('category, category_nm, cd, cd_nm') ->select('category, category_nm, cd, cd_nm')
->whereIn('category', $data) ->where('category', $category)
->where('use_yn', 'Y') ->where('use_yn', 'Y')
->orderBy('view_odr') ->orderBy('view_odr', 'asc')
->get() ->get()
->getResultArray(); ->getResultArray();
} }
public function getCategoryCodeList($category = [], $useYn = '')
public function getCodeLists(array $categories): array
{ {
$this->db->select('category, cd, cd_nm, use_yn'); if (empty($categories)) {
$this->db->from('codes'); return [];
$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(); $result = $this->db->table('codes')
->select('category, category_nm, cd, cd_nm')
->whereIn('category', $categories)
->where('use_yn', 'Y')
->orderBy('category', 'asc')
->orderBy('view_odr', 'asc')
->get()
->getResultArray();
//echo $this->db->last_query()."<br>"; $groupedData = [];
foreach ($result as $item) {
$category = $item['category'];
if (!isset($groupedData[$category])) {
$groupedData[$category] = [
'name' => $item['category_nm'],
'items' => []
];
}
$groupedData[$category]['items'][$item['cd']] = $item['cd_nm'];
}
return $groupedData;
}
public function getCategoryCodeList(array $category = [], string $useYn = ''): array
{
if (empty($category)) {
return [];
}
$builder = $this->db->table('codes');
$builder->select('category, cd, cd_nm, use_yn');
$builder->whereIn('category', $category);
if (!empty($useYn)) {
$builder->where('use_yn', $useYn);
}
$builder->orderBy('category', 'asc');
$builder->orderBy('view_odr', 'asc');
$query = $builder->get();
//여기 아래부분을 해줘야 배열을 카테고리로 뽑아쓸수있다 위에는 배열에 배열이담김
$codes = []; $codes = [];
foreach ($query->getResultArray() as $row) { foreach ($query->getResultArray() as $row) {
$codes[$row['category']][] = ['cd' => $row['cd'], 'cd_nm' => $row['cd_nm']]; $codes[$row['category']][] = ['cd' => $row['cd'], 'cd_nm' => $row['cd_nm']];
} }
return $codes; return $codes;
} }
/** /**
* 코드 상세 * 코드 상세
*/ */
public function getCodeDetail($category, $code) public function getCodeDetail(string $category, string $code): array
{ {
$sql = "SELECT category, category_nm, cd, cd_nm FROM codes" . return $this->db->table('codes')
" WHERE category = ? and cd = ?"; ->select('category, category_nm, cd, cd_nm')
$data = [$category, $code]; ->where('category', $category)
$query = $this->db->query($sql, $data); ->where('cd', $code)
$row = $query->getResultArray(); ->get()
->getResultArray();
return $row;
} }
} }

View File

@@ -99,10 +99,11 @@ class AssignModel extends Model
public function getTotalCount($data) public function getTotalCount($data)
{ {
$sql = "SELECT $sql = "SELECT
COUNT(*) AS cnt COUNT(DISTINCT b.usr_sq) AS cnt
FROM result a FROM result a
INNER JOIN users b ON b.usr_sq = a.usr_sq INNER JOIN users b ON b.usr_sq = a.usr_sq
INNER JOIN receipt d ON d.rcpt_sq = a.rcpt_sq
INNER JOIN departments c ON c.dept_sq = a.dept_sq
WHERE 1=1 "; WHERE 1=1 ";
@@ -138,13 +139,13 @@ class AssignModel extends Model
if (!empty($data['srchTxt'])) { if (!empty($data['srchTxt'])) {
if ($data['srchType'] === "1") { if ($data['srchType'] === "1") {
$sql .= "AND usr_id like CONCAT('%', '{$data['srchTxt']}', '%' ) "; $sql .= "AND b.usr_id like CONCAT('%', '{$data['srchTxt']}', '%' ) ";
} else if ($data['srchType'] === "2") { } else if ($data['srchType'] === "2") {
$sql .= "AND usr_nm like CONCAT('%', '{$data['srchTxt']}', '%' ) "; $sql .= "AND b.usr_nm like CONCAT('%', '{$data['srchTxt']}', '%' ) ";
} else { } else {
$sql .= "AND ( $sql .= "AND (
usr_id like CONCAT('%', '{$data['srchTxt']}', '%' ) b.usr_id like CONCAT('%', '{$data['srchTxt']}', '%' )
OR usr_nm like CONCAT('%', '{$data['srchTxt']}', '%' ) OR b.usr_nm like CONCAT('%', '{$data['srchTxt']}', '%' )
) "; ) ";
} }
} }
@@ -157,6 +158,10 @@ class AssignModel extends Model
public function getUserList($start, $end, $data) public function getUserList($start, $end, $data)
{ {
$sql = "SELECT $sql = "SELECT
sub.*,
COUNT(*) OVER() as total_count
FROM (
SELECT
b.usr_nm, b.usr_id, b.usr_sq b.usr_nm, b.usr_id, b.usr_sq
, SUM(CASE WHEN a.rsrv_tm_hour IN ('00','01','02','03','04','05','06','07','08','09','10','11','12','13','14','15','16','17','18','19','20','21','22','23','24') THEN 1 ELSE 0 END) TODAY , SUM(CASE WHEN a.rsrv_tm_hour IN ('00','01','02','03','04','05','06','07','08','09','10','11','12','13','14','15','16','17','18','19','20','21','22','23','24') THEN 1 ELSE 0 END) TODAY
, SUM(CASE WHEN a.rsrv_tm_ap = 'AM' AND a.rsrv_tm_hour = '09' THEN 1 ELSE 0 END) AM09 , SUM(CASE WHEN a.rsrv_tm_ap = 'AM' AND a.rsrv_tm_hour = '09' THEN 1 ELSE 0 END) AM09
@@ -174,7 +179,8 @@ class AssignModel extends Model
, SUM(CASE WHEN a.rsrv_tm_ap = 'PM' AND a.rsrv_tm_hour IN ('00','08','09','10','11','12','20','21','22','23','24') THEN 1 ELSE 0 END) PMETC , SUM(CASE WHEN a.rsrv_tm_ap = 'PM' AND a.rsrv_tm_hour IN ('00','08','09','10','11','12','20','21','22','23','24') THEN 1 ELSE 0 END) PMETC
FROM result a FROM result a
INNER JOIN users b ON b.usr_sq = a.usr_sq INNER JOIN users b ON b.usr_sq = a.usr_sq
INNER JOIN receipt d ON d.rcpt_sq = a.rcpt_sq
INNER JOIN departments c ON c.dept_sq = a.dept_sq
WHERE 1=1 "; WHERE 1=1 ";
@@ -210,20 +216,20 @@ class AssignModel extends Model
if (!empty($data['srchTxt'])) { if (!empty($data['srchTxt'])) {
if ($data['srchType'] === "1") { if ($data['srchType'] === "1") {
$sql .= "AND usr_id like CONCAT('%', '{$data['srchTxt']}', '%' ) "; $sql .= "AND b.usr_id like CONCAT('%', '{$data['srchTxt']}', '%' ) ";
} else if ($data['srchType'] === "2") { } else if ($data['srchType'] === "2") {
$sql .= "AND usr_nm like CONCAT('%', '{$data['srchTxt']}', '%' ) "; $sql .= "AND b.usr_nm like CONCAT('%', '{$data['srchTxt']}', '%' ) ";
} else { } else {
$sql .= "AND ( $sql .= "AND (
usr_id like CONCAT('%', '{$data['srchTxt']}', '%' ) b.usr_id like CONCAT('%', '{$data['srchTxt']}', '%' )
OR usr_nm like CONCAT('%', '{$data['srchTxt']}', '%' ) OR b.usr_nm like CONCAT('%', '{$data['srchTxt']}', '%' )
) "; ) ";
} }
} }
$sql .= "GROUP BY b.usr_id, b.usr_nm "; $sql .= "GROUP BY b.usr_id, b.usr_nm
) sub
$sql .= "LIMIT {$start}, {$end}"; LIMIT {$start}, {$end}";
$query = $this->db->query($sql); $query = $this->db->query($sql);

View File

@@ -0,0 +1,119 @@
<?php
namespace App\Models\v2;
use CodeIgniter\Model;
/**
* BaseV2Model
* V2 모듈 공통 메서드: 지역/본부/팀/담당자 조회
* 각 모듈 Model은 이 클래스를 상속받아 getTotalCount/getResultList/getExcelList 등 모듈 고유 메서드만 구현합니다.
*/
abstract class BaseV2Model extends Model
{
// 지역 목록 조회
public function getAreaList($sido = '', $gugun = '')
{
if (!empty($gugun)) {
$gugun = substr($gugun, '0', '5');
$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,5),'00000')" .
" WHERE a.region_cd LIKE concat(?, '%')" .
" AND a.region_cd NOT LIKE '%00000'" .
" AND a.region_cd LIKE '%00'" .
" AND a.use_yn = 'Y'" .
" ORDER BY a.region_nm ASC";
$query = $this->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();
}
}

View File

@@ -2,121 +2,11 @@
namespace App\Models\v2; namespace App\Models\v2;
use App\Models\webfax\FaxModel; use App\Models\webfax\FaxModel;
use CodeIgniter\Model; use App\Models\v2\BaseV2Model;
class M701Model extends Model class M701Model extends BaseV2Model
{ {
// 지역 목록 조회
public function getAreaList($sido = '', $gugun = '')
{
if (!empty($gugun)) {
$gugun = substr($gugun, '0', '5');
$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,5),'00000')" .
" WHERE a.region_cd LIKE concat(?, '%')" .
" AND a.region_cd NOT LIKE '%00000'" .
" AND a.region_cd LIKE '%00'" .
" AND a.use_yn = 'Y'" .
" ORDER BY a.region_nm ASC";
$query = $this->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 getTotalCount($data) public function getTotalCount($data)
{ {
$sql = "SELECT $sql = "SELECT
@@ -144,7 +34,7 @@ class M701Model extends Model
// 매물번호 // 매물번호
if (!empty($data['atcl_no'])) { if (!empty($data['atcl_no'])) {
$sql .= "AND a.atcl = '{$data['atcl_no']}' "; $sql .= "AND a.atcl_no = '{$data['atcl_no']}' ";
} else { } else {
// 현재상태 // 현재상태
if (!empty($data['stat_cd'])) { if (!empty($data['stat_cd'])) {
@@ -364,7 +254,7 @@ class M701Model extends Model
// 매물번호 // 매물번호
if (!empty($data['atcl_no'])) { if (!empty($data['atcl_no'])) {
$sql .= "AND a.atcl = '{$data['atcl_no']}' "; $sql .= "AND a.atcl_no = '{$data['atcl_no']}' ";
} else { } else {
// 현재상태 // 현재상태
if (!empty($data['stat_cd'])) { if (!empty($data['stat_cd'])) {
@@ -553,7 +443,7 @@ class M701Model extends Model
// 매물번호 // 매물번호
if (!empty($data['atcl_no'])) { if (!empty($data['atcl_no'])) {
$sql .= "AND a.atcl = '{$data['atcl_no']}' "; $sql .= "AND a.atcl_no = '{$data['atcl_no']}' ";
} else { } else {
// 현재상태 // 현재상태
if (!empty($data['stat_cd'])) { if (!empty($data['stat_cd'])) {

View File

@@ -2,119 +2,10 @@
namespace App\Models\v2; namespace App\Models\v2;
use App\Models\webfax\FaxModel; use App\Models\webfax\FaxModel;
use CodeIgniter\Model; use App\Models\v2\BaseV2Model;
class M702Model extends Model class M702Model extends BaseV2Model
{ {
// 지역 목록 조회
public function getAreaList($sido = '', $gugun = '')
{
if (!empty($gugun)) {
$gugun = substr($gugun, '0', '5');
$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,5),'00000')" .
" WHERE a.region_cd LIKE concat(?, '%')" .
" AND a.region_cd NOT LIKE '%00000'" .
" AND a.region_cd LIKE '%00'" .
" AND a.use_yn = 'Y'" .
" ORDER BY a.region_nm ASC";
$query = $this->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 getTotalCount($data) public function getTotalCount($data)
{ {

View File

@@ -3,119 +3,10 @@ namespace App\Models\v2;
use App\Models\receipt\ReceiptModel; use App\Models\receipt\ReceiptModel;
use App\Models\webfax\FaxModel; use App\Models\webfax\FaxModel;
use CodeIgniter\Model; use App\Models\v2\BaseV2Model;
class M703Model extends Model class M703Model extends BaseV2Model
{ {
// 지역 목록 조회
public function getAreaList($sido = '', $gugun = '')
{
if (!empty($gugun)) {
$gugun = substr($gugun, '0', '5');
$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,5),'00000')" .
" WHERE a.region_cd LIKE concat(?, '%')" .
" AND a.region_cd NOT LIKE '%00000'" .
" AND a.region_cd LIKE '%00'" .
" AND a.use_yn = 'Y'" .
" ORDER BY a.region_nm ASC";
$query = $this->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 getTotalCount($data) public function getTotalCount($data)
{ {

View File

@@ -2,119 +2,10 @@
namespace App\Models\v2; namespace App\Models\v2;
use App\Models\webfax\FaxModel; use App\Models\webfax\FaxModel;
use CodeIgniter\Model; use App\Models\v2\BaseV2Model;
class M704Model extends Model class M704Model extends BaseV2Model
{ {
// 지역 목록 조회
public function getAreaList($sido = '', $gugun = '')
{
if (!empty($gugun)) {
$gugun = substr($gugun, '0', '5');
$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,5),'00000')" .
" WHERE a.region_cd LIKE concat(?, '%')" .
" AND a.region_cd NOT LIKE '%00000'" .
" AND a.region_cd LIKE '%00'" .
" AND a.use_yn = 'Y'" .
" ORDER BY a.region_nm ASC";
$query = $this->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 getTotalCount($data) public function getTotalCount($data)
{ {

View File

@@ -1,119 +1,10 @@
<?php <?php
namespace App\Models\v2; namespace App\Models\v2;
use CodeIgniter\Model; use App\Models\v2\BaseV2Model;
class M705Model extends Model class M705Model extends BaseV2Model
{ {
// 지역 목록 조회
public function getAreaList($sido = '', $gugun = '')
{
if (!empty($gugun)) {
$gugun = substr($gugun, '0', '5');
$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,5),'00000')" .
" WHERE a.region_cd LIKE concat(?, '%')" .
" AND a.region_cd NOT LIKE '%00000'" .
" AND a.region_cd LIKE '%00'" .
" AND a.use_yn = 'Y'" .
" ORDER BY a.region_nm ASC";
$query = $this->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 getTotalCount($data) public function getTotalCount($data)
{ {

View File

@@ -2,119 +2,10 @@
namespace App\Models\v2; namespace App\Models\v2;
use App\Models\webfax\FaxModel; use App\Models\webfax\FaxModel;
use CodeIgniter\Model; use App\Models\v2\BaseV2Model;
class M706Model extends Model class M706Model extends BaseV2Model
{ {
// 지역 목록 조회
public function getAreaList($sido = '', $gugun = '')
{
if (!empty($gugun)) {
$gugun = substr($gugun, '0', '5');
$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,5),'00000')" .
" WHERE a.region_cd LIKE concat(?, '%')" .
" AND a.region_cd NOT LIKE '%00000'" .
" AND a.region_cd LIKE '%00'" .
" AND a.use_yn = 'Y'" .
" ORDER BY a.region_nm ASC";
$query = $this->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 getTotalCount($data) public function getTotalCount($data)
{ {

View File

@@ -3,119 +3,10 @@ namespace App\Models\v2;
use App\Models\receipt\ReceiptModel; use App\Models\receipt\ReceiptModel;
use App\Models\webfax\FaxModel; use App\Models\webfax\FaxModel;
use CodeIgniter\Model; use App\Models\v2\BaseV2Model;
class M708Model extends Model class M708Model extends BaseV2Model
{ {
// 지역 목록 조회
public function getAreaList($sido = '', $gugun = '')
{
if (!empty($gugun)) {
$gugun = substr($gugun, '0', '5');
$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,5),'00000')" .
" WHERE a.region_cd LIKE concat(?, '%')" .
" AND a.region_cd NOT LIKE '%00000'" .
" AND a.region_cd LIKE '%00'" .
" AND a.use_yn = 'Y'" .
" ORDER BY a.region_nm ASC";
$query = $this->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 getTotalCount($data) public function getTotalCount($data)
{ {

View File

@@ -1,119 +1,10 @@
<?php <?php
namespace App\Models\v2; namespace App\Models\v2;
use CodeIgniter\Model; use App\Models\v2\BaseV2Model;
class M709Model extends Model class M709Model extends BaseV2Model
{ {
// 지역 목록 조회
public function getAreaList($sido = '', $gugun = '')
{
if (!empty($gugun)) {
$gugun = substr($gugun, '0', '5');
$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,5),'00000')" .
" WHERE a.region_cd LIKE concat(?, '%')" .
" AND a.region_cd NOT LIKE '%00000'" .
" AND a.region_cd LIKE '%00'" .
" AND a.use_yn = 'Y'" .
" ORDER BY a.region_nm ASC";
$query = $this->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 getDepartmentPath($usr_sq) public function getDepartmentPath($usr_sq)

View File

@@ -1,121 +1,11 @@
<?php <?php
namespace App\Models\v2; namespace App\Models\v2;
use CodeIgniter\Model; use App\Models\v2\BaseV2Model;
class M710Model extends Model class M710Model extends BaseV2Model
{ {
// 지역 목록 조회
public function getAreaList($sido = '', $gugun = '')
{
if (!empty($gugun)) {
$gugun = substr($gugun, '0', '5');
$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,5),'00000')" .
" WHERE a.region_cd LIKE concat(?, '%')" .
" AND a.region_cd NOT LIKE '%00000'" .
" AND a.region_cd LIKE '%00'" .
" AND a.use_yn = 'Y'" .
" ORDER BY a.region_nm ASC";
$query = $this->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 getTotalCount($data) public function getTotalCount($data)
{ {
$sql = "SELECT $sql = "SELECT

View File

@@ -1,9 +1,9 @@
<?php <?php
namespace App\Models\v2; namespace App\Models\v2;
use CodeIgniter\Model; use App\Models\v2\BaseV2Model;
class M711Model extends Model class M711Model extends BaseV2Model
{ {
} }

View File

@@ -1,119 +1,10 @@
<?php <?php
namespace App\Models\v2; namespace App\Models\v2;
use CodeIgniter\Model; use App\Models\v2\BaseV2Model;
class M712Model extends Model class M712Model extends BaseV2Model
{ {
// 지역 목록 조회
public function getAreaList($sido = '', $gugun = '')
{
if (!empty($gugun)) {
$gugun = substr($gugun, '0', '5');
$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,5),'00000')" .
" WHERE a.region_cd LIKE concat(?, '%')" .
" AND a.region_cd NOT LIKE '%00000'" .
" AND a.region_cd LIKE '%00'" .
" AND a.use_yn = 'Y'" .
" ORDER BY a.region_nm ASC";
$query = $this->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 getTotalCount($data) public function getTotalCount($data)
{ {

View File

@@ -1,119 +1,10 @@
<?php <?php
namespace App\Models\v2; namespace App\Models\v2;
use CodeIgniter\Model; use App\Models\v2\BaseV2Model;
class M713Model extends Model class M713Model extends BaseV2Model
{ {
// 지역 목록 조회
public function getAreaList($sido = '', $gugun = '')
{
if (!empty($gugun)) {
$gugun = substr($gugun, '0', '5');
$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,5),'00000')" .
" WHERE a.region_cd LIKE concat(?, '%')" .
" AND a.region_cd NOT LIKE '%00000'" .
" AND a.region_cd LIKE '%00'" .
" AND a.use_yn = 'Y'" .
" ORDER BY a.region_nm ASC";
$query = $this->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 getTotalCount($data) public function getTotalCount($data)
{ {

View File

@@ -40,43 +40,79 @@ class TypeSHandler
{ {
CLI::write(CLI::color('🟢 Type S 처리 시작 :: ' . $articleNumber, 'green')); CLI::write(CLI::color('🟢 Type S 처리 시작 :: ' . $articleNumber, 'green'));
// CI 트랜잭션 strict 모드에서는 이전 실패 상태가 누적될 수 있으므로 시작 전에 초기화
if (method_exists($this->db, 'resetTransStatus')) {
$this->db->resetTransStatus();
}
$this->db->transBegin(); $this->db->transBegin();
try { try {
// 1. Receipt 데이터 저장 // 1. Receipt 데이터 저장
$receiptData = $this->parameterMapper->mapReceipt($articleNumber, $rawData, $payload); $receiptData = $this->parameterMapper->mapReceipt($articleNumber, $rawData, $payload);
write_custom_log("Result Insert 데이터: " . json_encode($receiptData, JSON_UNESCAPED_UNICODE), 'INFO', 'service');
if (!$this->receiptModel->insert($receiptData)) { if (!$this->receiptModel->insert($receiptData)) {
$lastQuery = (string)$this->receiptModel->getLastQuery();
$errors = json_encode($this->receiptModel->errors());
write_custom_log("Receipt Insert 실패 | SQL: $lastQuery | Errors: $errors", 'ERROR', 'service');
throw new Exception("Receipt Insert 실패: " . json_encode($this->receiptModel->errors())); throw new Exception("Receipt Insert 실패: " . json_encode($this->receiptModel->errors()));
} }
$rcptSq = $this->receiptModel->getInsertID(); $rcptSq = $this->receiptModel->getInsertID();
$receiptInsertSql = (string)$this->receiptModel->getLastQuery(); // Receipt SQL 캡처
CLI::write(CLI::color("✅ Receipt 저장 성공 (ID: $rcptSq)", 'blue')); CLI::write(CLI::color("✅ Receipt 저장 성공 (ID: $rcptSq)", 'blue'));
// 2. Result 데이터 저장 // 2. Result 데이터 저장
$resultData = $this->parameterMapper->mapResult($rcptSq, $rawData); $resultData = $this->parameterMapper->mapResult($rcptSq, $rawData);
write_custom_log("Result Insert 데이터: " . json_encode($resultData, JSON_UNESCAPED_UNICODE), 'INFO', 'service');
if (!$this->resultModel->insert($resultData)) { if (!$this->resultModel->insert($resultData)) {
throw new Exception("Result Insert 실패"); $lastQuery = (string)$this->resultModel->getLastQuery();
$errors = json_encode($this->resultModel->errors());
write_custom_log("Result Insert 실패 | SQL: $lastQuery | Errors: $errors", 'ERROR', 'service');
throw new Exception("Result Insert 실패: $errors");
} }
$resultInsertSql = (string)$this->resultModel->getLastQuery(); // Result SQL 캡처
write_custom_log("Result Insert 성공 | SQL: $resultInsertSql", 'INFO', 'service');
CLI::write(CLI::color('✅ Result 저장 성공', 'blue')); CLI::write(CLI::color('✅ Result 저장 성공', 'blue'));
// 3. 트랜잭션 커밋 // 3. 트랜잭션 커밋
$this->db->transComplete(); $this->db->transComplete();
if ($this->db->transStatus() === false) { if ($this->db->transStatus() === false) {
write_custom_log("Type S DB 트랜잭션 최종 실패", 'ERROR', 'service'); $dbError = $this->db->error();
$errorJson = json_encode($dbError, JSON_UNESCAPED_UNICODE);
$receiptLastQuery = (string)$this->receiptModel->getLastQuery();
$resultLastQuery = (string)$this->resultModel->getLastQuery();
write_custom_log(
"Type S DB 트랜잭션 최종 실패 | DB Error: {$errorJson} | ReceiptLastQuery: {$receiptLastQuery} | ResultLastQuery: {$resultLastQuery}",
'ERROR',
'service'
);
throw new Exception("Type S DB 트랜잭션 최종 실패"); throw new Exception("Type S DB 트랜잭션 최종 실패");
} }
// 4. 로그 기록 // 4. 로그 기록
write_custom_log("Type S 처리 성공 | Atcl: $articleNumber | Rcpt_sq: $rcptSq", 'INFO', 'service'); write_custom_log("Type S 처리 성공 | Atcl: $articleNumber | Rcpt_sq: $rcptSq", 'INFO', 'service');
write_custom_log("Receipt Insert SQL: " . (string)$this->receiptModel->getLastQuery(), 'INFO', 'service'); write_custom_log("Receipt Insert SQL: " . $receiptInsertSql, 'INFO', 'service');
write_custom_log("Result Insert SQL: " . (string)$this->resultModel->getLastQuery(), 'INFO', 'service'); write_custom_log("Result Insert SQL: " . $resultInsertSql, 'INFO', 'service');
// 5. 네이버 예약 정보 동기화 (비동기) // 5. 네이버 예약 정보 동기화 (비동기)
try { // try {
$syncResult = $this->naverClient->submitSyncResult($rawData['reserveNo'] ?? ''); // // $syncResult = $this->naverClient->submitSyncResult($rawData['reserveNo'] ?? '');
write_custom_log("Naver Sync Result Response: " . json_encode($syncResult), 'INFO', 'service'); // $rsrv_date = $receiptData['rsrv_date'] ?? null;
} catch (Exception $e) { // $reportData = [
write_custom_log("Naver Sync 실패 (계속 진행): " . $e->getMessage(), 'WARN', 'service'); // 'type' => 'S11',
} // 'code' => '10000',
// 'message' => '현장확인 예약',
// 'reserveDate' => $rsrv_date
// ];
// $syncResult = $this->naverClient->verificationSiteReport($articleNumber, $reportData);
// write_custom_log("Naver Sync Result Response: " . json_encode($syncResult), 'INFO', 'service');
// } catch (Exception $e) {
// write_custom_log("Naver Sync 실패 (계속 진행): " . $e->getMessage(), 'WARN', 'service');
// }
return $rcptSq; return $rcptSq;

View File

@@ -46,9 +46,11 @@ class TypeV2Handler
public function handle(string $articleNumber, array $rawData, array $payload): int public function handle(string $articleNumber, array $rawData, array $payload): int
{ {
CLI::write(CLI::color('🟢 Type V2 처리 시작 :: ' . $articleNumber, 'green')); CLI::write(CLI::color('🟢 Type V2 처리 시작 :: ' . $articleNumber, 'green'));
write_custom_log("TypeV2Handler 진입 | Atcl: $articleNumber | Payload: " . json_encode($payload), 'INFO', 'service');
try { try {
$requestType = $payload['requestType'] ?? 'REG'; $requestType = $payload['requestType'] ?? 'REG';
CLI::write(CLI::color("🔵 RequestType: $requestType", 'cyan'));
switch ($requestType) { switch ($requestType) {
case 'REG': case 'REG':
@@ -62,7 +64,9 @@ class TypeV2Handler
} }
} catch (Exception $e) { } catch (Exception $e) {
write_custom_log("Type V2 처리 실패: " . $e->getMessage(), 'ERROR', 'service'); $errorDetail = "Type V2 처리 실패 | Atcl: $articleNumber | Error: " . $e->getMessage() . " | File: " . $e->getFile() . ":" . $e->getLine();
write_custom_log($errorDetail, 'ERROR', 'service');
CLI::write(CLI::color("" . $errorDetail, 'red'));
throw $e; throw $e;
} }
} }
@@ -136,7 +140,9 @@ class TypeV2Handler
// 기존 검증 요청 확인 // 기존 검증 요청 확인
$existing = $this->vrfcReqModel->where('atcl_no', $articleNumber)->first(); $existing = $this->vrfcReqModel->where('atcl_no', $articleNumber)->first();
if (!$existing) { if (!$existing) {
throw new Exception("수정할 기존 데이터가 없습니다. Atcl: $articleNumber"); CLI::write(CLI::color("⚠️ 수정할 기존 데이터가 없음 → 신규 등록으로 전환 (Atcl: $articleNumber)", 'yellow'));
write_custom_log("MOD → REG 자동 전환 | Atcl: $articleNumber | 사유: 기존 데이터 없음", 'WARNING', 'service');
return $this->handleRegister($articleNumber, $rawData, $payload);
} }
$vrSq = $existing['vr_sq']; $vrSq = $existing['vr_sq'];
$stat_cd = $existing['stat_cd']; $stat_cd = $existing['stat_cd'];
@@ -199,7 +205,9 @@ class TypeV2Handler
// 기존 검증 요청 확인 // 기존 검증 요청 확인
$existing = $this->vrfcReqModel->where('atcl_no', $articleNumber)->first(); $existing = $this->vrfcReqModel->where('atcl_no', $articleNumber)->first();
if (!$existing) { if (!$existing) {
throw new Exception("취소할 기존 데이터가 없습니다. Atcl: $articleNumber"); CLI::write(CLI::color("⚠️ 취소할 데이터가 없음 → 무시 (Atcl: $articleNumber)", 'yellow'));
write_custom_log("CNC 무시 | Atcl: $articleNumber | 사유: 기존 데이터 없음", 'WARNING', 'service');
return 0; // 취소할 데이터가 없으므로 0 반환
} }
$vrSq = $existing['vr_sq']; $vrSq = $existing['vr_sq'];

View File

@@ -13,7 +13,7 @@ use Exception;
* 네이버 부동산 매물 처리 서비스 * 네이버 부동산 매물 처리 서비스
* *
* 네이버 API 응답을 받아서 타입별 처리 로직으로 위임하는 오케스트레이터 역할 * 네이버 API 응답을 받아서 타입별 처리 로직으로 위임하는 오케스트레이터 역할
* - Type S: 현장확인 (A01) * - Type S, S_VR: 현장확인 (A01)
* - Type V2: 일반/서류/비공동 (D04, F01 등) * - Type V2: 일반/서류/비공동 (D04, F01 등)
*/ */
class NaverService class NaverService
@@ -59,19 +59,33 @@ class NaverService
$vType = $rawData['verificationTypeCode'] ?? ''; $vType = $rawData['verificationTypeCode'] ?? '';
// 2. 원본 데이터 Staging 저장 // 2. 원본 데이터 Staging 저장
$this->rawStagingModel->insert([ $insertResult = $this->rawStagingModel->insert([
'atcl_no' => $articleNumber, 'atcl_no' => $articleNumber,
'verification_type' => $vType, 'verification_type' => $vType,
'request_type' => $requestType, 'request_type' => $requestType,
'raw_json' => $rawData 'raw_json' => $rawData
]); ]);
if (!$insertResult) {
$dbError = $this->db->error();
write_custom_log("naver_raw_staging Insert 실패 | Atcl: $articleNumber | Error: " . json_encode($dbError), 'ERROR', 'service');
throw new Exception("naver_raw_staging 저장 실패: " . json_encode($dbError));
}
CLI::write(CLI::color('🟢 임시테이블 저장 완료', 'green')); CLI::write(CLI::color('🟢 임시테이블 저장 완료', 'green'));
// 3. 타입별 분기 처리 // 3. 타입별 분기 처리
if ($vType === 'S') { CLI::write(CLI::color("🔵 검증타입 확인: vType = $vType", 'cyan'));
write_custom_log("타입별 분기 | Atcl: $articleNumber | vType: $vType | requestType: $requestType", 'INFO', 'service');
if ($vType === 'S' || $vType === 'S_VR') {
CLI::write(CLI::color('🟢 Type S Handler 호출', 'green'));
return $this->typeSHandler->handle($articleNumber, $rawData, $payload); return $this->typeSHandler->handle($articleNumber, $rawData, $payload);
} else { } else {
return $this->typeV2Handler->handle($articleNumber, $rawData, $payload); CLI::write(CLI::color('🟢 Type V2 Handler 호출', 'green'));
$result = $this->typeV2Handler->handle($articleNumber, $rawData, $payload);
write_custom_log("TypeV2Handler 완료 | Atcl: $articleNumber | Result: $result", 'INFO', 'service');
return $result;
} }
} catch (Exception $e) { } catch (Exception $e) {

View File

@@ -2,12 +2,20 @@
namespace App\Services\ParameterMapper; namespace App\Services\ParameterMapper;
use App\Models\Entities\RegionModel;
/** /**
* Type S 파라미터 매퍼 * Type S 파라미터 매퍼
* 현장확인 매물 (A01 등) 데이터를 Receipt 테이블용 파라미터로 변환 * 현장확인 매물 (A01 등) 데이터를 Receipt 테이블용 파라미터로 변환
*/ */
class TypeSParameterMapper extends BaseParameterMapper class TypeSParameterMapper extends BaseParameterMapper
{ {
private $regionModel;
public function __construct()
{
$this->regionModel = new RegionModel();
}
/** /**
* Receipt 테이블용 파라미터 생성 * Receipt 테이블용 파라미터 생성
*/ */
@@ -24,8 +32,8 @@ class TypeSParameterMapper extends BaseParameterMapper
// 평면도 여부 결정 // 평면도 여부 결정
$groundPlan = in_array($rawData['realEstateTypeCode'] ?? '', ['C01', 'C02']) ? 'N' : 'Y'; $groundPlan = in_array($rawData['realEstateTypeCode'] ?? '', ['C01', 'C02']) ? 'N' : 'Y';
// 거래 유형 매핑 // 거래 유형 코드 (tradeTypeCode 우선, 없으면 tradeType 한글을 변환)
$tradeType = $this->mapTradeType($rawData['tradeType'] ?? null); $tradeType = $rawData['tradeTypeCode'] ?? $this->mapTradeType($rawData['tradeType'] ?? null);
return [ return [
'comp_sq' => '2', 'comp_sq' => '2',
@@ -38,10 +46,11 @@ class TypeSParameterMapper extends BaseParameterMapper
'rcpt_product_info2' => $price['dealAmount'] ?? '0', 'rcpt_product_info2' => $price['dealAmount'] ?? '0',
'rcpt_product_info4' => $price['preSaleAmount'] ?? '0', 'rcpt_product_info4' => $price['preSaleAmount'] ?? '0',
'rcpt_product_info5' => $price['premiumAmount'] ?? '0', 'rcpt_product_info5' => $price['premiumAmount'] ?? '0',
'rcpt_product_info6' => $price['preSaleOptionAmount'] ?? '0',
'rcpt_living_yn' => ($rawData['site']['isRegistration'] ?? false) ? 'Y' : 'N', 'rcpt_living_yn' => ($rawData['site']['isRegistration'] ?? false) ? 'Y' : 'N',
'rcpt_agent' => $realtor['realtorName'] ?? null, 'rcpt_agent' => $realtor['realtorName'] ?? null,
'rcpt_sido' => mb_substr($address['legalDivision']['cityNumber'] ?? '', 0, 5), 'rcpt_sido' => $address['legalDivision']['cityNumber'] ?? null,
'rcpt_gugun' => mb_substr($address['legalDivision']['divisionNumber'] ?? '', 0, 10), 'rcpt_gugun' => $address['legalDivision']['divisionNumber'] ?? null,
'rcpt_dong' => $address['legalDivision']['sectorNumber'] ?? null, 'rcpt_dong' => $address['legalDivision']['sectorNumber'] ?? null,
'rcpt_hscp_nm' => $address['complexName'] ?? null, 'rcpt_hscp_nm' => $address['complexName'] ?? null,
'rcpt_hscp_no' => $address['complexNumber'] ?? null, 'rcpt_hscp_no' => $address['complexNumber'] ?? null,
@@ -87,6 +96,11 @@ class TypeSParameterMapper extends BaseParameterMapper
'rcpt_cpid' => $rawData['cpId'] ?? 'naver', 'rcpt_cpid' => $rawData['cpId'] ?? 'naver',
'isSiteVRVerification' => ($rawData['site']['isVrVerification'] ?? false) ? 'Y' : 'N', 'isSiteVRVerification' => ($rawData['site']['isVrVerification'] ?? false) ? 'Y' : 'N',
'isPromotionApply' => ($rawData['site']['isVrRepresentativeApply'] ?? false) ? 'Y' : 'N', 'isPromotionApply' => ($rawData['site']['isVrRepresentativeApply'] ?? false) ? 'Y' : 'N',
'dealAmount' => $price['dealAmount'] ?? null,
'warrantyAmount' => $price['warrantyAmount'] ?? null,
'leaseAmount' => $price['leaseAmount'] ?? null,
'preSaleAmount' => $price['preSaleAmount'] ?? null,
'premiumAmount' => $price['premiumAmount'] ?? null
]; ];
} }
@@ -97,9 +111,51 @@ class TypeSParameterMapper extends BaseParameterMapper
{ {
$now = db_now(); $now = db_now();
// VR 검증 여부에 따른 담당자 설정 $address = $rawData['address'] ?? [];
$deptSq = ($rawData['site']['isVrVerification'] ?? false) ? '29' : null; $sectorNumber = $address['legalDivision']['sectorNumber'] ?? null;
$usrSq = ($rawData['site']['isVrVerification'] ?? false) ? '1993' : null;
// VR 검증 여부 확인
$isVrVerification = ($rawData['site']['isVrVerification'] ?? false);
// 1. 지역별 담당자 조회
$charge = null;
if ($sectorNumber) {
$charge = $this->regionModel->getChargeByRegionCd($sectorNumber);
log_message('info', "[TypeSParameterMapper] 지역코드: {$sectorNumber}, 조회 결과: " . json_encode($charge, JSON_UNESCAPED_UNICODE));
}
// 2. 기본 담당자 설정 (지역별 담당자가 없으면)
$deptSq = $charge['dept_sq'] ?? '26';
$usrSq = $charge['usr_sq'] ?? '1';
// 담당자 존재 여부 검증 (users 테이블 확인)
if ($usrSq && $usrSq !== '1') {
$db = \Config\Database::connect();
$userExists = $db->table('users')->where('usr_sq', $usrSq)->countAllResults() > 0;
if (!$userExists) {
log_message('warning', "[TypeSParameterMapper] usr_sq={$usrSq}가 users 테이블에 없음. 기본값(1)으로 폴백");
$usrSq = '1';
$deptSq = '26';
}
}
log_message('info', "[TypeSParameterMapper] 기본 담당자 - dept_sq: {$deptSq}, usr_sq: {$usrSq}, VR검증: " . ($isVrVerification ? 'Y' : 'N'));
// 3. VR 검증인 경우 환경별 담당자로 덮어쓰기
if ($isVrVerification) {
log_message('info', "[TypeSParameterMapper] ENVIRONMENT 상수 값: " . ENVIRONMENT);
if (ENVIRONMENT === 'development') {
$deptSq = '29';
$usrSq = '472'; // vradmin
log_message('info', "[TypeSParameterMapper] VR 검증 (DEV) - dept_sq: {$deptSq}, usr_sq: {$usrSq}");
} else {
// production - TODO: 실제 프로덕션 VR 담당자로 변경 필요
$deptSq = '29';
$usrSq = '472'; // 임시: vradmin (프로덕션 VR 담당자 확인 후 수정)
log_message('warning', "[TypeSParameterMapper] VR 검증 (PROD) - 프로덕션 VR 담당자 미설정, 임시 담당자 사용: usr_sq={$usrSq}");
}
}
return [ return [
'rcpt_sq' => $rcptSq, 'rcpt_sq' => $rcptSq,

View File

@@ -90,6 +90,7 @@ class TypeV2ParameterMapper extends BaseParameterMapper
'lease_amt' => $price['leaseAmount'] ?? 0, 'lease_amt' => $price['leaseAmount'] ?? 0,
'isale_amt' => $price['preSaleAmount'] ?? 0, 'isale_amt' => $price['preSaleAmount'] ?? 0,
'prem_amt' => $price['premiumAmount'] ?? 0, 'prem_amt' => $price['premiumAmount'] ?? 0,
'preopt_amt' => $price['preSaleOptionAmount'] ?? 0,
'sise' => null, 'sise' => null,
'floor' => $floor['correspondenceFloorCount'] ?? null, 'floor' => $floor['correspondenceFloorCount'] ?? null,
'floor2' => $floor['totalFloorCount'] ?? null, 'floor2' => $floor['totalFloorCount'] ?? null,

View File

@@ -55,3 +55,5 @@
<link href="/architectui/assets/styles/vendors.98288b227c064e6a107f.css" rel="stylesheet"> <link href="/architectui/assets/styles/vendors.98288b227c064e6a107f.css" rel="stylesheet">
<link href="/architectui/assets/styles/main.98288b227c064e6a107f.css" rel="stylesheet"> <link href="/architectui/assets/styles/main.98288b227c064e6a107f.css" rel="stylesheet">
<link href="/architectui/assets/styles/custom.css" rel="stylesheet">
<link href="/common/css/custom.css" rel="stylesheet">

View File

@@ -3,6 +3,7 @@
<head> <head>
<?= $this->include('layouts/header') ?> <?= $this->include('layouts/header') ?>
<?= $this->renderSection('page_styles') ?>
</head> </head>
<body> <body>
@@ -148,6 +149,8 @@
}); });
</script> </script>
<?= $this->renderSection('page_scripts') ?>
</body> </body>
</html> </html>

View File

@@ -0,0 +1,2 @@
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/2.0.7/css/dataTables.bootstrap5.min.css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">

View File

@@ -0,0 +1,3 @@
<script src="https://cdn.datatables.net/2.0.7/js/dataTables.min.js"></script>
<script src="https://cdn.datatables.net/2.0.7/js/dataTables.bootstrap5.min.js"></script>
<script defer src="/architectui/assets/js/datatable.kor.js"></script>

View File

@@ -0,0 +1,2 @@
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/2.0.7/css/dataTables.dataTables.min.css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">

View File

@@ -0,0 +1,2 @@
<script src="https://cdn.datatables.net/2.0.7/js/dataTables.min.js"></script>
<script defer src="/architectui/assets/js/datatable.kor.js"></script>

View File

@@ -0,0 +1,67 @@
<script type="text/javascript">
/**
* DataTables v2 layout topEnd 버튼 헬퍼
* @param {Object} options - 옵션 객체
* @param {boolean} options.showSendButton - 등기부등본 전송 버튼 표시 여부 (기본값: false)
* @param {string} options.sendButtonText - 전송 버튼 텍스트
* @param {boolean} options.showExcelButton - 엑셀 버튼 표시 여부 (기본값: true)
* @param {string} options.excelButtonId - 엑셀 버튼 id
* @param {string} options.excelButtonClass - 엑셀 버튼 class
* @param {string} options.excelButtonHtml - 엑셀 버튼 innerHTML
* @returns {Function} DataTable layout.topEnd 콜백 함수
*/
function v2TopEndButtons(options) {
const opts = options || {};
return function () {
const container = document.createElement('div');
container.className = 'd-flex';
container.style.gap = '8px';
container.style.justifyContent = 'flex-end';
if (opts.showSendButton === true) {
const btnSend = document.createElement('button');
btnSend.className = 'btn btn-sm btn-outline-light';
btnSend.textContent = opts.sendButtonText || '등기부등본 전송';
container.append(btnSend);
}
if (opts.showExcelButton !== false) {
const btnExcel = document.createElement('button');
btnExcel.id = opts.excelButtonId || 'excel-download';
btnExcel.className = opts.excelButtonClass || 'btn btn-sm btn-outline-success';
btnExcel.innerHTML = opts.excelButtonHtml || '<i class="fa fa-fw fa-file-excel-o" aria-hidden="true"></i> 엑셀다운로드';
container.append(btnExcel);
}
return container;
};
}
/**
* DataTables v2 layout bottomStart/bottomEnd 통합 설정
* @param {Object} options - 옵션 객체
* @param {boolean} options.hasPageLength - pageLength 표시 여부 (기본값: true)
* @param {boolean} options.hasInfo - info 표시 여부 (기본값: true)
* @returns {Object} { bottomStart, bottomEnd } 객체
*/
function v2BottomLayout(options) {
const opts = options || {};
const hasPageLength = opts.hasPageLength !== false;
const hasInfo = opts.hasInfo !== false;
const config = {};
if (hasPageLength && hasInfo) {
config.bottomStart = ['pageLength', 'info'];
} else if (hasPageLength) {
config.bottomStart = 'pageLength';
} else if (hasInfo) {
config.bottomStart = 'info';
}
config.bottomEnd = 'paging';
return config;
}
</script>

View File

@@ -0,0 +1,410 @@
<?= $this->extend('layouts/main') ?>
<?= $this->section('page_styles') ?>
<style>
.stats-card {
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.stats-card h3 {
margin: 0 0 10px 0;
font-size: 14px;
color: #666;
}
.stats-card .number {
font-size: 32px;
font-weight: bold;
margin: 0;
}
.stats-card.total { background: #f8f9fa; border-left: 4px solid #6c757d; }
.stats-card.retryable { background: #d1ecf1; border-left: 4px solid #17a2b8; }
.stats-card.exhausted { background: #f8d7da; border-left: 4px solid #dc3545; }
.error-type-badge {
display: inline-block;
padding: 4px 12px;
border-radius: 12px;
font-size: 12px;
font-weight: 500;
margin-right: 8px;
}
.severity-high { background: #dc3545; color: white; }
.severity-medium { background: #ffc107; color: #000; }
.severity-low { background: #28a745; color: white; }
.solution-box {
background: #e7f3ff;
border-left: 4px solid #007bff;
padding: 15px;
margin-top: 10px;
border-radius: 4px;
}
.solution-box h5 {
margin: 0 0 10px 0;
color: #004085;
}
.solution-box ul {
margin: 0;
padding-left: 20px;
}
.log-table {
font-size: 13px;
}
.log-table th {
background: #f8f9fa;
font-weight: 600;
}
.log-table .error-msg {
max-width: 300px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
cursor: pointer;
}
.log-table .error-msg:hover {
overflow: visible;
white-space: normal;
}
.badge-retry-count {
background: #6c757d;
color: white;
padding: 2px 8px;
border-radius: 10px;
font-size: 11px;
}
.btn-retry-selected {
position: sticky;
top: 20px;
z-index: 100;
}
</style>
<?= $this->endSection() ?>
<?= $this->section('content') ?>
<div class="app-page-title">
<div class="page-title-wrapper">
<div class="page-title-heading">
<div class="page-title-icon">
<i class="pe-7s-attention icon-gradient bg-mean-fruit"></i>
</div>
<div>
Worker 실패 로그 관리
<div class="page-title-subheading">
처리 실패한 작업을 분석하고 재처리합니다.
</div>
</div>
</div>
</div>
</div>
<!-- 통계 카드 -->
<div class="row mb-4">
<div class="col-md-4">
<div class="stats-card total">
<h3>전체 실패 건수</h3>
<p class="number"><?= number_format($stats['total_fail']) ?></p>
</div>
</div>
<div class="col-md-4">
<div class="stats-card retryable">
<h3>재시도 가능</h3>
<p class="number"><?= number_format($stats['retry_available']) ?></p>
</div>
</div>
<div class="col-md-4">
<div class="stats-card exhausted">
<h3>재시도 횟수 초과</h3>
<p class="number"><?= number_format($stats['retry_exhausted']) ?></p>
</div>
</div>
</div>
<!-- 오류 유형별 분석 -->
<div class="main-card mb-3 card">
<div class="card-header">오류 유형별 분석</div>
<div class="card-body">
<div class="row">
<?php foreach ($errorTypes as $key => $type): ?>
<?php if ($type['count'] > 0): ?>
<div class="col-md-4 mb-3">
<span class="error-type-badge severity-<?= $type['severity'] ?>">
<?= $type['label'] ?>
</span>
<strong><?= number_format($type['count']) ?>건</strong>
</div>
<?php endif; ?>
<?php endforeach; ?>
</div>
</div>
</div>
<!-- 실패 로그 목록 -->
<div class="main-card mb-3 card">
<div class="card-header">
<div class="d-flex justify-content-between align-items-center">
<span>최근 실패 로그 (50건)</span>
<button class="btn btn-primary btn-sm" id="retrySelected" disabled>
<i class="fa fa-refresh"></i> 선택 항목 재처리
</button>
</div>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover log-table">
<thead>
<tr>
<th width="40">
<input type="checkbox" id="selectAll">
</th>
<th width="80">ID</th>
<th width="120">매물번호</th>
<th width="100">오류 유형</th>
<th>오류 메시지</th>
<th width="80">재시도</th>
<th width="150">발생시각</th>
<th width="100">액션</th>
</tr>
</thead>
<tbody>
<?php foreach ($logs as $log): ?>
<tr>
<td>
<?php if ($log['can_retry']): ?>
<input type="checkbox" class="log-checkbox" value="<?= $log['seq'] ?>">
<?php else: ?>
<i class="fa fa-ban text-muted" title="재시도 불가"></i>
<?php endif; ?>
</td>
<td><?= $log['seq'] ?></td>
<td><?= $log['atcl_no'] ?? '-' ?></td>
<td>
<?php
$typeInfo = $errorTypes[$log['error_type']] ?? ['label' => '기타', 'severity' => 'low'];
?>
<span class="error-type-badge severity-<?= $typeInfo['severity'] ?>">
<?= $typeInfo['label'] ?>
</span>
</td>
<td>
<div class="error-msg" title="<?= esc($log['error_msg']) ?>">
<?= esc($log['error_msg']) ?>
</div>
</td>
<td class="text-center">
<?php if ($log['retry_cnt'] > 0): ?>
<span class="badge-retry-count"><?= $log['retry_cnt'] ?>회</span>
<?php else: ?>
-
<?php endif; ?>
</td>
<td><?= date('m-d H:i', strtotime($log['created_at'])) ?></td>
<td>
<button class="btn btn-sm btn-info view-detail" data-id="<?= $log['seq'] ?>">
상세
</button>
</td>
</tr>
<?php endforeach; ?>
<?php if (empty($logs)): ?>
<tr>
<td colspan="8" class="text-center text-muted py-4">
실패한 로그가 없습니다.
</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
<?= $this->endSection() ?>
<?= $this->section('modals') ?>
<!-- 상세 모달 -->
<div class="modal fade" id="detailModal" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">로그 상세 정보</h5>
<button type="button" class="close" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<div class="modal-body" id="detailContent">
<div class="text-center py-4">
<div class="spinner-border" role="status"></div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">닫기</button>
<button type="button" class="btn btn-primary" id="retryOne" style="display:none;">
<i class="fa fa-refresh"></i> 재처리
</button>
</div>
</div>
</div>
</div>
<?= $this->endSection() ?>
<?= $this->section('page_scripts') ?>
<script>
$(document).ready(function() {
// 전체 선택
$('#selectAll').on('change', function() {
$('.log-checkbox').prop('checked', this.checked);
updateRetryButton();
});
// 개별 선택
$('.log-checkbox').on('change', function() {
updateRetryButton();
});
// 재처리 버튼 활성화/비활성화
function updateRetryButton() {
const checked = $('.log-checkbox:checked').length;
$('#retrySelected').prop('disabled', checked === 0);
}
// 선택 항목 재처리
$('#retrySelected').on('click', function() {
const logIds = $('.log-checkbox:checked').map(function() {
return $(this).val();
}).get();
if (logIds.length === 0) {
Swal.fire('알림', '재처리할 항목을 선택해주세요.', 'warning');
return;
}
Swal.fire({
title: '재처리 확인',
text: `선택한 ${logIds.length}건을 재처리하시겠습니까?`,
icon: 'question',
showCancelButton: true,
confirmButtonText: '재처리',
cancelButtonText: '취소'
}).then((result) => {
if (result.isConfirmed) {
retryLogs(logIds);
}
});
});
// 재처리 실행
function retryLogs(logIds) {
const btn = $('#retrySelected');
btn.prop('disabled', true).html('<i class="fa fa-spinner fa-spin"></i> 처리 중...');
$.ajax({
url: '<?= base_url('manage/worker/retry') ?>',
method: 'POST',
data: { log_ids: logIds },
dataType: 'json',
success: function(response) {
if (response.success) {
const results = response.results;
let message = `성공: ${results.success}건, 실패: ${results.fail}건\n\n`;
results.details.forEach(detail => {
const icon = detail.status === 'success' ? '✅' :
detail.status === 'skip' ? '⏭' : '❌';
message += `${icon} [${detail.atcl_no}] ${detail.message}\n`;
});
Swal.fire({
title: '재처리 완료',
text: message,
icon: results.success > 0 ? 'success' : 'warning',
preConfirm: () => location.reload()
});
} else {
Swal.fire('오류', response.message, 'error');
}
},
error: function() {
Swal.fire('오류', '재처리 중 오류가 발생했습니다.', 'error');
},
complete: function() {
btn.prop('disabled', false).html('<i class="fa fa-refresh"></i> 선택 항목 재처리');
}
});
}
// 상세 보기
$('.view-detail').on('click', function() {
const logId = $(this).data('id');
$('#detailContent').html('<div class="text-center py-4"><div class="spinner-border"></div></div>');
$('#detailModal').modal('show');
$.ajax({
url: `<?= base_url('manage/worker/detail') ?>/${logId}`,
method: 'GET',
dataType: 'json',
success: function(response) {
if (response.success) {
displayDetail(response.log);
} else {
$('#detailContent').html('<div class="alert alert-danger">' + response.message + '</div>');
}
},
error: function() {
$('#detailContent').html('<div class="alert alert-danger">로그를 불러오는 중 오류가 발생했습니다.</div>');
}
});
});
// 상세 정보 표시
function displayDetail(log) {
const solution = log.solution;
const errorType = log.error_type;
let html = `
<div class="mb-3">
<strong>로그 ID:</strong> ${log.seq}<br>
<strong>매물번호:</strong> ${log.atcl_no || '-'}<br>
<strong>상태:</strong> <span class="badge badge-danger">${log.status}</span><br>
<strong>재시도 횟수:</strong> ${log.retry_cnt || 0}회<br>
<strong>발생시각:</strong> ${log.created_at}
</div>
<div class="mb-3">
<strong>오류 메시지:</strong>
<div class="alert alert-danger">${log.error_msg}</div>
</div>
<div class="solution-box mb-3">
<h5>${solution.title}</h5>
<ul>
${solution.steps.map(step => `<li>${step}</li>`).join('')}
</ul>
</div>
<div class="mb-3">
<strong>원본 Payload:</strong>
<pre class="bg-light p-3" style="max-height: 300px; overflow-y: auto;">${JSON.stringify(log.parsed_payload, null, 2)}</pre>
</div>
`;
$('#detailContent').html(html);
// 재처리 버튼 표시
if (log.can_retry) {
$('#retryOne').show().off('click').on('click', function() {
$('#detailModal').modal('hide');
retryLogs([log.seq]);
});
} else {
$('#retryOne').hide();
}
}
});
</script>
<?= $this->endSection() ?>

View File

@@ -2675,9 +2675,10 @@ $usr_level = session('usr_level');
title: "정상 처리되었습니다.", title: "정상 처리되었습니다.",
icon: "success", icon: "success",
draggable: true draggable: true
}) }).then(() => {
// 녹취파일 체크박스 활성화 및 체크
location.reload(); $('#chk_record').prop('disabled', false).prop('checked', true);
});
} else { } else {
Swal.fire({ Swal.fire({
title: result.msg, title: result.msg,

View File

@@ -7,12 +7,19 @@ $usr_nm = session('usr_nm');
<?= $this->extend('layouts/main') ?> <?= $this->extend('layouts/main') ?>
<?= $this->section('content') ?> <?= $this->section('content') ?>
<style> <style>
th { th {
font-size: 11px; font-size: 11px;
text-align: center; text-align: center;
} }
/* 테이블 헤더 좌우 여백 조정 */
#resultList thead th {
padding-left: 4px !important;
padding-right: 4px !important;
}
td { td {
text-align: center; text-align: center;
} }
@@ -65,19 +72,47 @@ $usr_nm = session('usr_nm');
overflow-x: auto !important; overflow-x: auto !important;
} }
/* 테이블 폭: 내용 기준으로 커지되, 최소는 100% */ /* 테이블 폭: 내용에 따라 자동 조정, 스크롤 필요시 표시 */
.table-responsive #resultList { .table-responsive #resultList {
width: max-content !important; width: 100% !important;
min-width: 100% !important;
table-layout: auto !important; table-layout: auto !important;
} }
/* 줄바꿈 금지 */ /* 줄바꿈 허용 - 공백 기준으로 줄바꿈 */
#resultList th, #resultList th,
#resultList td { #resultList td {
white-space: nowrap; white-space: normal !important;
word-wrap: break-word;
} }
/* tw-* 클래스가 DataTable의 inline 스타일보다 우선순위 높이기 */
#resultList th.tw-30,
#resultList td.tw-30 { width: 30px !important; max-width: 30px !important; min-width: 30px !important; }
#resultList th.tw-50,
#resultList td.tw-50 { width: 50px !important; max-width: 50px !important; min-width: 50px !important; }
#resultList th.tw-70,
#resultList td.tw-70 { width: 70px !important; max-width: 70px !important; min-width: 70px !important; }
#resultList th.tw-80,
#resultList td.tw-80 { width: 80px !important; max-width: 80px !important; min-width: 80px !important; }
#resultList th.tw-90,
#resultList td.tw-90 { width: 90px !important; max-width: 90px !important; min-width: 90px !important; }
#resultList th.tw-100,
#resultList td.tw-100 { width: 100px !important; max-width: 100px !important; min-width: 100px !important; }
#resultList th.tw-120,
#resultList td.tw-120 { width: 120px !important; max-width: 120px !important; min-width: 120px !important; }
#resultList th.tw-130,
#resultList td.tw-130 { width: 130px !important; max-width: 130px !important; min-width: 130px !important; }
#resultList th.tw-140,
#resultList td.tw-140 { width: 140px !important; max-width: 140px !important; min-width: 140px !important; }
#resultList th.tw-150,
#resultList td.tw-150 { width: 150px !important; max-width: 150px !important; min-width: 150px !important; }
#resultList th.tw-180,
#resultList td.tw-180 { width: 180px !important; max-width: 180px !important; min-width: 180px !important; }
#resultList th.tw-200,
#resultList td.tw-200 { width: 200px !important; max-width: 200px !important; min-width: 200px !important; }
#resultList th.tw-250,
#resultList td.tw-250 { width: 250px !important; max-width: 250px !important; min-width: 250px !important; }
/* PC에서 가로 스크롤이 잘리는 대표 구간들 강제 해제 */ /* PC에서 가로 스크롤이 잘리는 대표 구간들 강제 해제 */
.main-card, .main-card,
.card, .card,
@@ -100,133 +135,115 @@ $usr_nm = session('usr_nm');
.card-body { .card-body {
min-width: 0; min-width: 0;
} }
</style>
<h1>조직별 배정 현황</h1> /* 검색 영역 행마다 구분선 추가 */
#frm_srch_info .row.g-3 {
padding: 12px 0;
border-bottom: 1px solid #e9ecef;
}
#frm_srch_info .row.g-3:last-child {
border-bottom: none;
}
</style>
<div class="col-md-12 col-xl-12"> <div class="col-md-12 col-xl-12">
<div class="main-card mb-3 card"> <div class="main-card mb-3 card">
<div class="card-header bg-white border-bottom shadow-sm">
<div class="d-flex flex-wrap align-items-center gap-3 card-header-tab">
<div>
<h4 class="mb-0 fw-bold text-dark">조직별 배정 현황</h4>
</div>
</div>
</div>
<div class="card-body"> <div class="card-body">
<ul class="nav nav-tabs">
<li class="nav-item"><a data-bs-toggle="tab" href="#tab-eg10-0" class="active nav-link">검색</a></li>
<li class="nav-item"><a data-bs-toggle="tab" href="#tab-eg10-1" class="nav-link">조직별 통계</a></li>
</ul>
<div class="tab-content">
<div class="tab-pane active" id="tab-eg10-0" role="tabpanel">
<form id="frm_srch_info" method="get" onsubmit="return false;"> <form id="frm_srch_info" method="get" onsubmit="return false;">
<input type="hidden" name="m" id="m" value="M801"> <!-- 검색 폼 -->
<input type="hidden" name="todo" id="todo" value="inq"> <div class="row g-3">
<input type="hidden" name="usr_id" value=""> <div class="col-md-1">
<div class="card mb-3">
<div class="card-body">
<div class="row g-3 align-items-end">
<!-- 매물ID -->
<div class="col-12 col-md-2 col-lg-1">
<label class="form-label mb-1">매물ID</label> <label class="form-label mb-1">매물ID</label>
<input type="text" class="form-control" name="rcpt_atclno" id="rcpt_atclno" <input type="text" class="form-control form-control-sm" name="rcpt_atclno" id="rcpt_atclno"
onkeypress="hscp_no_enter(event)"> onkeypress="hscp_no_enter(event)">
</div> </div>
<!-- 지역구분 --> <div class="col-md-4">
<div class="col-12 col-md-6 col-lg-3">
<label class="form-label mb-1">지역구분</label>
<div class="row g-2">
<div class="col-4">
<select class="form-select" name="srcSido" id="srcSido">
<option value="">시/도</option>
<?php foreach ($sido as $s): ?>
<option value="<?= $s['region_cd'] ?>"><?= $s['region_nm'] ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-4">
<select class="form-select" name="srcGugun" id="srcGugun">
<option value="">시/군/구</option>
</select>
</div>
<div class="col-4">
<select class="form-select" name="srcDong" id="srcDong">
<option value="">읍/면/동</option>
</select>
</div>
</div>
</div>
<!-- 접수일자 -->
<div class="col-12 col-md-6 col-lg-3">
<label class="form-label mb-1">접수일자</label>
<div class="input-group">
<input type="date" class="form-control" name="sdate" id="sdate" placeholder="시작일">
<span class="input-group-text">~</span>
<input type="date" class="form-control" name="date" id="edate" placeholder="종료일">
</div>
</div>
<!-- 예약일자 -->
<div class="col-12 col-md-6 col-lg-4">
<label class="form-label mb-1">예약일자</label> <label class="form-label mb-1">예약일자</label>
<div class="input-group"> <div class="input-group input-group-sm">
<select class="form-select" name="rsrv_tm_ap"> <select class="form-select form-select-sm" name="rsrv_tm_ap">
<option value="">오전/오후</option> <option value="">오전/오후</option>
<option value="AM">오전</option> <option value="AM">오전</option>
<option value="PM">오후</option> <option value="PM">오후</option>
</select> </select>
<input type="date" class="form-control" name="rsrv_sdate" id="rsrv_sdate" placeholder="시작일"> <input type="date" class="form-control form-control-sm" name="rsrv_sdate" id="rsrv_sdate" placeholder="시작일">
<span class="input-group-text">~</span> <span class="input-group-text">~</span>
<input type="date" class="form-control" name="rsrv_edate" id="rsrv_edate" placeholder="종료일"> <input type="date" class="form-control form-control-sm" name="rsrv_edate" id="rsrv_edate" placeholder="종료일">
</div> </div>
</div> </div>
<!-- 방문담당 --> <div class="col-md-3">
<div class="col-12 col-lg-4">
<label class="form-label mb-1">관할조직</label> <label class="form-label mb-1">관할조직</label>
<div class="row g-2"> <div class="d-flex gap-1">
<div class="col-4"> <select name="bonbu" id="bonbu" class="form-select form-select-sm">
<select class="form-select" name="bonbu" id="bonbu">
<option value="">-본부-</option> <option value="">-본부-</option>
<?php foreach ($bonbu as $d): ?> <?php foreach ($bonbu as $d): ?>
<option value="<?= $d['dept_sq'] ?>"><?= $d['dept_nm'] ?></option> <option value="<?= $d['dept_sq'] ?>">
<?= $d['dept_nm'] ?>
</option>
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
</div> <select name="team" id="team" class="form-select form-select-sm">
<div class="col-4">
<select class="form-select" name="team" id="team">
<option value="">-팀-</option> <option value="">-팀-</option>
</select> </select>
</div> <select name="damdang" id="damdang" class="form-select form-select-sm">
<div class="col-4">
<select class="form-select" name="damdang" id="damdang">
<option value="">-담당자-</option> <option value="">-담당자-</option>
</select> </select>
</div> </div>
</div> </div>
<div class="col-md-3">
<label class="form-label mb-1">지역별조회</label>
<div class="d-flex gap-1">
<select name="srcSido" id="srcSido" class="form-select form-select-sm">
<option value="">-시/도-</option>
<?php foreach ($sido as $s): ?>
<option value="<?= $s['region_cd'] ?>">
<?= $s['region_nm'] ?>
</option>
<?php endforeach; ?>
</select>
<select name="srcGugun" id="srcGugun" class="form-select form-select-sm">
<option value="">-시/군/구-</option>
</select>
<select name="srcDong" id="srcDong" class="form-select form-select-sm">
<option value="">-읍/면/동-</option>
</select>
</div>
</div> </div>
<div class="col-12 col-md-2 col-lg-1"> <div class="col-md-1">
<label class="form-label mb-1">평면도 유무</label> <label class="form-label mb-1">평면도유무</label>
<select class="form-select" name="ground_plan_yn"> <select class="form-select form-select-sm" name="ground_plan_yn">
<option value="">선택</option> <option value="">전체</option>
<option value="Y">Y</option>
<option value="N">N</option>
</select>
</div>
</div>
<div class="row g-3">
<div class="col-md-1">
<label class="form-label mb-1">평면도요청</label>
<select class="form-select form-select-sm" name="ground_plan">
<option value="">전체</option>
<option value="Y">Y</option> <option value="Y">Y</option>
<option value="N">N</option> <option value="N">N</option>
</select> </select>
</div> </div>
<div class="col-12 col-md-2 col-lg-1"> <div class="col-md-1">
<label class="form-label mb-1">평면도 요청</label>
<select class="form-select" name="ground_plan">
<option value="">선택</option>
<option value="Y">Y</option>
<option value="N">N</option>
</select>
</div>
<div class="col-12 col-md-2 col-lg-1">
<label class="form-label mb-1">직거래</label> <label class="form-label mb-1">직거래</label>
<select class="form-select" name="direct_trad_yn"> <select class="form-select form-select-sm" name="direct_trad_yn">
<option value="">선택</option> <option value="">전체</option>
<option value="Y">Y</option> <option value="Y">Y</option>
<option value="N">N</option> <option value="N">N</option>
</select> </select>
@@ -235,7 +252,7 @@ $usr_nm = session('usr_nm');
<!-- 검색유형 --> <!-- 검색유형 -->
<div class="col-md-1"> <div class="col-md-1">
<label class="form-label mb-1">검색유형</label> <label class="form-label mb-1">검색유형</label>
<select class="form-select" name="srchType"> <select class="form-select form-select-sm" name="srchType">
<option value="">선택</option> <option value="">선택</option>
<option value="1">중개사명</option> <option value="1">중개사명</option>
<option value="2">중개사연락처</option> <option value="2">중개사연락처</option>
@@ -245,14 +262,13 @@ $usr_nm = session('usr_nm');
<!-- 검색어 --> <!-- 검색어 -->
<div class="col-md-2"> <div class="col-md-2">
<label class="form-label mb-1">검색어</label> <label class="form-label mb-1">검색어</label>
<input type="text" class="form-control" name="srchTxt" placeholder="검색어 입력"> <input type="text" class="form-control form-control-sm" name="srchTxt" placeholder="검색어 입력">
</div> </div>
<!-- 진행상태 + 검색버튼 --> <div class="col-md-6">
<div class="col-12 col-lg-10">
<label class="form-label mb-1">진행상태</label> <label class="form-label mb-1">진행상태</label>
<div class="d-flex flex-wrap gap-2 align-items-center border rounded p-2"> <div class="d-flex flex-wrap gap-2">
<div class="form-check me-2"> <div class="form-check">
<input class="form-check-input" type="checkbox" name="rcpt_stat_all" value="Y" <input class="form-check-input" type="checkbox" name="rcpt_stat_all" value="Y"
id="rcpt_stat_all" onclick="progressStatAll(this, this.checked);"> id="rcpt_stat_all" onclick="progressStatAll(this, this.checked);">
<label class="form-check-label" for="rcpt_stat_all">전체</label> <label class="form-check-label" for="rcpt_stat_all">전체</label>
@@ -270,81 +286,26 @@ $usr_nm = session('usr_nm');
</div> </div>
</div> </div>
<div class="col-12 col-lg-1 d-grid"> <div class="col-md-1 d-grid">
<label class="form-label mb-1 invisible">검색</label>
<button type="button" class="btn btn-primary" id="btnSearch"> <button type="button" class="btn btn-primary" id="btnSearch">
<i class="pe-7s-search me-1"></i>검색 <i class="pe-7s-search me-1"></i>검색
</button> </button>
</div> </div>
</div>
</div><!-- /row -->
</div><!-- /card-body -->
</div><!-- /card -->
</form> </form>
</div>
<div class="tab-pane" id="tab-eg10-1" role="tabpanel">
<div class="row">
<div class="col-md-3">
<div class="card mb-3" style="max-width: 380px;">
<div class="card-body p-0">
<div class="table-scroll">
<table class="table table-sm table-hover table-striped mb-0 align-middle text-center">
<thead class="table-light">
<tr>
<th style="width:42px;">
<input class="form-check-input" type="checkbox" id="depChkAll">
</th>
<th>관할본부</th>
<th>방문담당</th>
<th style="width:90px;">배정건수</th>
</tr>
</thead>
<tbody id="deptArea"></tbody>
</table>
</div>
</div>
<div class="card-footer d-flex justify-content-end gap-1">
<button type="button" class="btn btn-outline-secondary btn-sm" onclick="printMap()">
<i class="pe-7s-print"></i> 인쇄하기
</button>
<button type="button" class="btn btn-primary btn-sm" onclick="deptMap()">
<i class="pe-7s-map-2"></i> 지도로 보기
</button>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card mb-3" style="max-width: 380px;">
<div class="card-body p-0">
<div class="table-scroll">
<table class="table table-sm table-hover table-striped mb-0 align-middle text-center">
<thead class="table-light">
<tr>
<th>지역구분</th>
<th style="width:90px;">요청건수</th>
</tr>
</thead>
<tbody id="statsArea"></tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div> </div>
</div> </div>
</div>
</div>
</div>
<div class="col-md-12 col-xl-12">
<div class="main-card mb-3 card"> <div class="main-card mb-3 card">
<div class="card-header d-flex align-items-center"> <div class="card-header bg-white border-bottom shadow-sm">
<div class="d-flex align-items-center flex-wrap" style="gap:8px; flex:1;"> <div class="d-flex flex-wrap align-items-center w-100 justify-content-between card-header-tab">
<h5 class="mb-0 fw-bold text-dark">검색 결과</h5>
<select class="form-select form-select-sm" id="bonbu2" style="width:140px;"> <div class="d-flex align-items-center gap-2 ms-auto">
<!-- 배정변경 조직 선택 -->
<select class="form-select form-select-sm" id="bonbu2" style="width:120px;">
<option value="">-본부-</option> <option value="">-본부-</option>
<?php foreach ($bonbu as $d): ?> <?php foreach ($bonbu as $d): ?>
<option value="<?= $d['dept_sq'] ?>"> <option value="<?= $d['dept_sq'] ?>">
@@ -353,34 +314,36 @@ $usr_nm = session('usr_nm');
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
<select class="form-select form-select-sm" id="team2" style="width:160px;"> <select class="form-select form-select-sm" id="team2" style="width:140px;">
<option value="">-팀-</option> <option value="">-팀-</option>
</select> </select>
<select class="form-select form-select-sm" id="damdang2" style="width:160px;"> <select class="form-select form-select-sm" id="damdang2" style="width:140px;">
<option value="">-담당자-</option> <option value="">-담당자-</option>
</select> </select>
<button type="button" class="btn btn-sm btn-outline-primary" id="btn_part_change"> <button type="button" class="btn btn-sm btn-outline-primary" id="btn_part_change" onclick="updateAssign()">
배정변경 배정변경
</button> </button>
</div>
<div class="ml-auto">
<button class="btn btn-sm btn-outline-success" id="excel-download"> <button class="btn btn-sm btn-outline-success" id="excel-download">
<i class="fa fa-fw" aria-hidden="true" title="file-excel-o"></i> 엑셀다운로드 <i class="fa fa-fw" aria-hidden="true" title="file-excel-o"></i>
엑셀다운로드
</button> </button>
<button class="btn btn-sm btn-outline-secondary" id="excel-download2"> <button class="btn btn-sm btn-outline-secondary" id="excel-download2">
<i class="fa fa-fw" aria-hidden="true" title="file-excel-o"></i> 배정내역출력 <i class="fa fa-fw" aria-hidden="true" title="file-excel-o"></i>
배정내역출력
</button> </button>
</div> </div>
</div> </div>
</div>
<div class="card-body"> <div class="card-body">
<div class="table-responsive"> <div class="table-responsive">
<table id="resultList" class="table table-hover table-striped table-bordered"> <table id="resultList" class="table table-hover table-striped table-bordered">
<thead> <thead>
<th class="text-center"> <tr>
<th>
<input type="checkbox" class="form-check-input" name="chkAll" id="chkAll" /> <input type="checkbox" class="form-check-input" name="chkAll" id="chkAll" />
</th> </th>
<th>진행상태</th> <th>진행상태</th>
@@ -396,45 +359,18 @@ $usr_nm = session('usr_nm');
<th>평면도유무</th> <th>평면도유무</th>
<th>평면도요청</th> <th>평면도요청</th>
<th>면적확인</th> <th>면적확인</th>
</tr>
</thead> </thead>
<tbody> <tbody>
<!-- 여기는 비워둠: AJAX로 채움 -->
</tbody> </tbody>
</table> </table>
</div> </div>
</div>
<div class="card-footer justify-content-start gap-1">
<button type="button" class="btn btn-light">
예약 확인
</button>
<button type="button" class="btn btn-outline-light">
검수지연
</button>
<button type="button" class="btn btn-outline-light">
검수지연 삭제
</button>
</div> </div>
</div> </div>
</div> </div>
<?= $this->section('modals') ?>
<div class="modal fade" id="deptMapModal" tabindex="-1">
<div class="modal-dialog modal-xl">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">지도보기</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body p-0">
<div id="dialog-deptmap-content"></div>
</div>
</div>
</div>
</div>
<?= $this->endSection() ?>
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.13.6/css/jquery.dataTables.min.css" /> <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.13.6/css/jquery.dataTables.min.css" />
<script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script> <script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script>
<script defer src="/architectui/assets/js/datatable.kor.js"></script> <script defer src="/architectui/assets/js/datatable.kor.js"></script>
@@ -622,61 +558,6 @@ $usr_nm = session('usr_nm');
d.start = d.start || 0 d.start = d.start || 0
d.length = d.length || 10 d.length = d.length || 10
},
dataSrc: function (d) {
const deptList = d.widgets?.deptList;
const areaStats = d.widgets?.areaStats ?? [];
if (deptList.length > 0) {
var str = "";
for (var i = 0; i < deptList.length; i++) {
str += `
<tr>
<td><input class="form-check-input depChk" type="checkbox" name="depChk[]" value="${deptList[i].dept_sq}"></td>
<td>${deptList[i].bonbu_nm}</td>
<td>${deptList[i].team_nm}</td>
<td><span class="badge bg-primary">${deptList[i].cnt} 건</span></td>
</tr>
`;
}
$("#deptArea").html(str);
} else {
str = `
<tr>
<td colspan="4">조회 가능한 데이터가 없습니다.</td>
</tr>
`;
$("#deptArea").html(str);
}
if (areaStats.length > 0) {
var str = "";
for (var i = 0; i < areaStats.length; i++) {
str += `
<tr>
<td>${areaStats[i].rcpt_dong}</td>
<td><span class="badge bg-warning">${areaStats[i].cnt} 건</span></td>
</tr>
`;
}
$("#statsArea").html(str);
} else {
str = `
<tr>
<td colspan="2">조회 가능한 데이터가 없습니다.</td>
</tr>
`;
$("#statsArea").html(str);
}
return d.data;
} }
}, },
columnDefs: [ columnDefs: [
@@ -700,7 +581,7 @@ $usr_nm = session('usr_nm');
{ data: 'exp_spc_yn' }, { data: 'exp_spc_yn' },
], ],
// 옵션들 예시 // 옵션들 예시
pdestroy: true, destroy: true,
deferRender: true, deferRender: true,
scrollX: false, scrollX: false,
autoWidth: false, autoWidth: false,
@@ -737,18 +618,6 @@ $usr_nm = session('usr_nm');
$('#chkAll').prop('checked', total > 0 && total === checkedCnt); $('#chkAll').prop('checked', total > 0 && total === checkedCnt);
}); });
// 전체 선택
$(document).on('change', '#depChkAll', function () {
$('.depChk').prop('checked', this.checked);
});
// 개별 체크 시 전체 체크 동기화
$(document).on('change', '.depChk', function () {
const total = $('.depChk').length;
const checked = $('.depChk:checked').length;
$('#depChkAll').prop('checked', total === checked);
});
table.on('draw', function () { table.on('draw', function () {
$('#chkAll').prop('checked', false); $('#chkAll').prop('checked', false);
}); });
@@ -815,6 +684,15 @@ $usr_nm = session('usr_nm');
} }
function hscp_no_enter(event) {
if (event.keyCode == 13) {
table.ajax.reload()
}
}
function progressStatAll(el, checked) {
$('input[name="stat[]"]').prop('checked', checked);
}
/** datatable render */ /** datatable render */
function fn_chk_render(data, type, row, meta) { function fn_chk_render(data, type, row, meta) {
@@ -890,56 +768,9 @@ $usr_nm = session('usr_nm');
/** datatable render */ /** datatable render */
// 인쇄하기
function printMap() {
var data = $('input[name="depChk[]"]').serialize();
var usr_id = $('input[name="usr_id"]').serialize();
if (data == '') {
alert('팀을 선택해주세요');
return;
}
var url = '/article/dept/print?' + data;
window.open(url, '', '');
}
function deptMap() {
const data = $('input[name="depChk[]"]:checked').serialize();
if (!data) {
alert('팀을 선택해주세요');
return;
}
const url = '/article/dept/print?' + data;
$('#dialog-deptmap-content').html(
`<iframe style="border:0;width:100%;height:650px;" src="${url}"></iframe>`
);
const modal = new bootstrap.Modal(document.getElementById('deptMapModal'));
modal.show();
}
// 엑셀 다운로드 // 엑셀 다운로드
function downloadExcel(data) { function downloadExcel(data) {
const ws = XLSX.utils.json_to_sheet(data); const ws = XLSX.utils.json_to_sheet(data);
// ws['!cols'] = [
// { wpx: 100 },
// { wpx: 100 },
// { wpx: 100 },
// { wpx: 100 },
// { wpx: 150 },
// { wpx: 120 },
// { wpx: 100 },
// { wpx: 100 },
// { wpx: 100 },
// { wpx: 100 },
// { wpx: 100 },
// { wpx: 100 },
// ];
const wb = XLSX.utils.book_new(); const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "Sheet1"); XLSX.utils.book_append_sheet(wb, ws, "Sheet1");
@@ -1047,7 +878,7 @@ $usr_nm = session('usr_nm');
var arr = new Array(); var arr = new Array();
const bonbu = $("#bonbu2").val(); const bonbu = $("#bonbu2").val();
const team = $("#team2").val(); const team = $("#team2").val();
const user = $("#user2").val(); const user = $("#damdang2").val();
$('#resultList tbody .row-chk:checked').each(function () { $('#resultList tbody .row-chk:checked').each(function () {
const rowData = table.row($(this).closest('tr')).data(); const rowData = table.row($(this).closest('tr')).data();
@@ -1088,14 +919,12 @@ $usr_nm = session('usr_nm');
return; return;
} }
swal.fire({ Swal.fire({
text: "배정을 변경하시겠습니까?", text: "배정을 변경하시겠습니까?",
type: "warning", icon: "warning",
showCancelButton: true, showCancelButton: true,
confirmButtonText: "예", confirmButtonText: "예",
cancelButtonText: "아니오", cancelButtonText: "아니오",
closeOnConfirm: false,
closeOnCancel: true,
confirmButtonColor: "#3085d6", confirmButtonColor: "#3085d6",
cancelButtonColor: "#d33", cancelButtonColor: "#d33",
}).then((result) => { }).then((result) => {

File diff suppressed because it is too large Load Diff

View File

@@ -13,6 +13,12 @@ $usr_nm = session('usr_nm');
text-align: center; text-align: center;
} }
/* 테이블 헤더 좌우 여백 조정 */
#resultList thead th {
padding-left: 4px !important;
padding-right: 4px !important;
}
td { td {
text-align: center; text-align: center;
} }
@@ -65,19 +71,47 @@ $usr_nm = session('usr_nm');
overflow-x: auto !important; overflow-x: auto !important;
} }
/* 테이블 폭: 내용 기준으로 커지되, 최소는 100% */ /* 테이블 폭: 내용에 따라 자동 조정, 스크롤 필요시 표시 */
.table-responsive #resultList { .table-responsive #resultList {
width: max-content !important; width: 100% !important;
min-width: 100% !important;
table-layout: auto !important; table-layout: auto !important;
} }
/* 줄바꿈 금지 */ /* 줄바꿈 허용 - 공백 기준으로 줄바꿈 */
#resultList th, #resultList th,
#resultList td { #resultList td {
white-space: nowrap; white-space: normal !important;
word-wrap: break-word;
} }
/* tw-* 클래스가 DataTable의 inline 스타일보다 우선순위 높이기 */
#resultList th.tw-30,
#resultList td.tw-30 { width: 30px !important; max-width: 30px !important; min-width: 30px !important; }
#resultList th.tw-50,
#resultList td.tw-50 { width: 50px !important; max-width: 50px !important; min-width: 50px !important; }
#resultList th.tw-70,
#resultList td.tw-70 { width: 70px !important; max-width: 70px !important; min-width: 70px !important; }
#resultList th.tw-80,
#resultList td.tw-80 { width: 80px !important; max-width: 80px !important; min-width: 80px !important; }
#resultList th.tw-90,
#resultList td.tw-90 { width: 90px !important; max-width: 90px !important; min-width: 90px !important; }
#resultList th.tw-100,
#resultList td.tw-100 { width: 100px !important; max-width: 100px !important; min-width: 100px !important; }
#resultList th.tw-120,
#resultList td.tw-120 { width: 120px !important; max-width: 120px !important; min-width: 120px !important; }
#resultList th.tw-130,
#resultList td.tw-130 { width: 130px !important; max-width: 130px !important; min-width: 130px !important; }
#resultList th.tw-140,
#resultList td.tw-140 { width: 140px !important; max-width: 140px !important; min-width: 140px !important; }
#resultList th.tw-150,
#resultList td.tw-150 { width: 150px !important; max-width: 150px !important; min-width: 150px !important; }
#resultList th.tw-180,
#resultList td.tw-180 { width: 180px !important; max-width: 180px !important; min-width: 180px !important; }
#resultList th.tw-200,
#resultList td.tw-200 { width: 200px !important; max-width: 200px !important; min-width: 200px !important; }
#resultList th.tw-250,
#resultList td.tw-250 { width: 250px !important; max-width: 250px !important; min-width: 250px !important; }
/* PC에서 가로 스크롤이 잘리는 대표 구간들 강제 해제 */ /* PC에서 가로 스크롤이 잘리는 대표 구간들 강제 해제 */
.main-card, .main-card,
.card, .card,
@@ -100,12 +134,27 @@ $usr_nm = session('usr_nm');
.card-body { .card-body {
min-width: 0; min-width: 0;
} }
</style>
<h1>현장확인V2 조직별 배정 현황</h1> /* 검색 영역 행마다 구분선 추가 */
#frm_srch_info .row.g-3 {
padding: 12px 0;
border-bottom: 1px solid #e9ecef;
}
#frm_srch_info .row.g-3:last-child {
border-bottom: none;
}
</style>
<div class="col-md-12 col-xl-12"> <div class="col-md-12 col-xl-12">
<div class="main-card mb-3 card"> <div class="main-card mb-3 card">
<div class="card-header bg-white border-bottom shadow-sm">
<div class="d-flex flex-wrap align-items-center gap-3 card-header-tab">
<div>
<h4 class="mb-0 fw-bold text-dark">현장확인V2 조직별 배정 현황</h4>
</div>
</div>
</div>
<div class="card-body"> <div class="card-body">
<ul class="nav nav-tabs"> <ul class="nav nav-tabs">
<li class="nav-item"><a data-bs-toggle="tab" href="#tab-eg10-0" class="active nav-link">검색</a></li> <li class="nav-item"><a data-bs-toggle="tab" href="#tab-eg10-0" class="active nav-link">검색</a></li>

View File

@@ -14,6 +14,12 @@ $usr_nm = session('usr_nm');
text-align: center; text-align: center;
} }
/* 테이블 헤더 좌우 여백 조정 */
#resultList thead th {
padding-left: 4px !important;
padding-right: 4px !important;
}
td { td {
text-align: center; text-align: center;
} }
@@ -66,19 +72,47 @@ $usr_nm = session('usr_nm');
overflow-x: auto !important; overflow-x: auto !important;
} }
/* 테이블 폭: 내용 기준으로 커지되, 최소는 100% */ /* 테이블 폭: 내용에 따라 자동 조정, 스크롤 필요시 표시 */
.table-responsive #resultList { .table-responsive #resultList {
width: max-content !important; width: 100% !important;
min-width: 100% !important;
table-layout: auto !important; table-layout: auto !important;
} }
/* 줄바꿈 금지 */ /* 줄바꿈 허용 - 공백 기준으로 줄바꿈 */
#resultList th, #resultList th,
#resultList td { #resultList td {
white-space: nowrap; white-space: normal !important;
word-wrap: break-word;
} }
/* tw-* 클래스가 DataTable의 inline 스타일보다 우선순위 높이기 */
#resultList th.tw-30,
#resultList td.tw-30 { width: 30px !important; max-width: 30px !important; min-width: 30px !important; }
#resultList th.tw-50,
#resultList td.tw-50 { width: 50px !important; max-width: 50px !important; min-width: 50px !important; }
#resultList th.tw-70,
#resultList td.tw-70 { width: 70px !important; max-width: 70px !important; min-width: 70px !important; }
#resultList th.tw-80,
#resultList td.tw-80 { width: 80px !important; max-width: 80px !important; min-width: 80px !important; }
#resultList th.tw-90,
#resultList td.tw-90 { width: 90px !important; max-width: 90px !important; min-width: 90px !important; }
#resultList th.tw-100,
#resultList td.tw-100 { width: 100px !important; max-width: 100px !important; min-width: 100px !important; }
#resultList th.tw-120,
#resultList td.tw-120 { width: 120px !important; max-width: 120px !important; min-width: 120px !important; }
#resultList th.tw-130,
#resultList td.tw-130 { width: 130px !important; max-width: 130px !important; min-width: 130px !important; }
#resultList th.tw-140,
#resultList td.tw-140 { width: 140px !important; max-width: 140px !important; min-width: 140px !important; }
#resultList th.tw-150,
#resultList td.tw-150 { width: 150px !important; max-width: 150px !important; min-width: 150px !important; }
#resultList th.tw-180,
#resultList td.tw-180 { width: 180px !important; max-width: 180px !important; min-width: 180px !important; }
#resultList th.tw-200,
#resultList td.tw-200 { width: 200px !important; max-width: 200px !important; min-width: 200px !important; }
#resultList th.tw-250,
#resultList td.tw-250 { width: 250px !important; max-width: 250px !important; min-width: 250px !important; }
/* PC에서 가로 스크롤이 잘리는 대표 구간들 강제 해제 */ /* PC에서 가로 스크롤이 잘리는 대표 구간들 강제 해제 */
.main-card, .main-card,
.card, .card,
@@ -101,12 +135,27 @@ $usr_nm = session('usr_nm');
.card-body { .card-body {
min-width: 0; min-width: 0;
} }
</style>
<h1>평면도 관리</h1> /* 검색 영역 행마다 구분선 추가 */
#frm_srch_info .row.g-3 {
padding: 12px 0;
border-bottom: 1px solid #e9ecef;
}
#frm_srch_info .row.g-3:last-child {
border-bottom: none;
}
</style>
<div class="col-md-12 col-xl-12"> <div class="col-md-12 col-xl-12">
<div class="main-card mb-3 card"> <div class="main-card mb-3 card">
<div class="card-header bg-white border-bottom shadow-sm">
<div class="d-flex flex-wrap align-items-center gap-3 card-header-tab">
<div>
<h4 class="mb-0 fw-bold text-dark">평면도 관리</h4>
</div>
</div>
</div>
<div class="card-body"> <div class="card-body">
<form id="frm_srch_info" method="get" onsubmit="return false;"> <form id="frm_srch_info" method="get" onsubmit="return false;">
<div class="alert alert-warning py-2 mb-3"> <div class="alert alert-warning py-2 mb-3">

View File

@@ -4,21 +4,18 @@
table th { table th {
text-align: center; text-align: center;
} }
.tab-header {
display: inline;
}
</style> </style>
<h4 class="mb-3">처리가능 수량관리</h4>
<div class="col-12"> <div class="col-12">
<div class="main-card mb-3 card"> <div class="main-card mb-3 card">
<!-- 탭은 card-header 안에 --> <!-- 타이틀 -->
<div class="card-header tab-header pb-0"> <div class="card-header bg-white border-bottom shadow-sm">
<ul class="nav nav-tabs card-header-tabs"> <h4 class="mb-0 fw-bold text-dark">처리가능 수량관리</h4>
</div>
<!-- 탭 -->
<ul class="nav nav-tabs px-3 pt-3 bg-white border-bottom">
<li class="nav-item"> <li class="nav-item">
<a class="nav-link active" data-bs-toggle="tab" href="#tab-eg10-0">일자별 처리가능 수량</a> <a class="nav-link active" data-bs-toggle="tab" href="#tab-eg10-0">일자별 처리가능 수량</a>
</li> </li>
@@ -29,79 +26,16 @@
<a class="nav-link" data-bs-toggle="tab" href="#tab-eg10-2">기본 수량</a> <a class="nav-link" data-bs-toggle="tab" href="#tab-eg10-2">기본 수량</a>
</li> </li>
</ul> </ul>
</div>
<div class="card-body"> <div class="card-body">
<!-- 공통 검색/필터 영역 -->
<div class="d-flex flex-wrap align-items-end justify-content-between gap-2 mb-3">
<div class="d-flex flex-wrap align-items-end gap-2">
<label class="form-label mb-1 small me-2">조회조건</label>
<!-- 첫번째탭 -->
<div id="form1" class="d-flex flex-wrap align-items-end gap-2">
<div class="input-group input-group-sm" style="min-width: 320px;">
<input type="date" class="form-control" name="sdate" id="sdate">
<span class="input-group-text">~</span>
<input type="date" class="form-control" name="edate" id="edate">
</div>
</div>
<!-- 두번째탭 -->
<div id="form2" class="d-none">
<div class="d-flex align-items-end gap-1">
<select class="form-select form-select-sm" name="region2" id="region2" style="min-width: 180px;">
<option value="">지역 선택</option>
<?php foreach ($sido as $s): ?>
<option value="<?= $s['region_cd'] ?>" <?php if ($s['region_cd'] == "1100000000") {
echo "selected";
} ?>>
<?= $s['region_nm'] ?>
</option>
<?php endforeach; ?>
</select>
<div class="input-group input-group-sm" style="min-width: 180px;">
<input type="date" class="form-control" name="sdate2" id="sdate2">
</div>
</div>
</div>
<!-- 세번째탭 -->
<div id="form3" class="d-none">
<div class="d-flex align-items-end gap-1">
<select class="form-select form-select-sm" name="region3" id="region3" style="min-width: 180px;">
<option value="">지역 선택</option>
<?php foreach ($sido as $s): ?>
<option value="<?= $s['region_cd'] ?>" <?php if ($s['region_cd'] == "1100000000") {
echo "selected";
} ?>>
<?= $s['region_nm'] ?>
</option>
<?php endforeach; ?>
</select>
</div>
</div>
</div>
<div class="d-flex gap-1 ms-auto">
<button class="btn btn-sm btn-outline-success" id="excel-download" type="button">
<i class="fa fa-fw" aria-hidden="true" title="file-excel-o"></i> 엑셀다운로드
</button>
<button class="btn btn-sm btn-outline-light" type="button" id="btnSearch">
조회
</button>
</div>
</div>
<!-- 탭 컨텐츠는 tab-content 바로 아래에 --> <!-- 탭 컨텐츠는 tab-content 바로 아래에 -->
<div class="tab-content"> <div class="tab-content">
<!-- 1) 일자별 --> <!-- 1) 일자별 -->
<div class="tab-pane fade show active" id="tab-eg10-0" role="tabpanel"> <div class="tab-pane fade show active" id="tab-eg10-0" role="tabpanel">
<div class="border rounded p-3 bg-white"> <div class="border rounded p-3 bg-white">
<table class="table table-sm table-hover table-striped mb-0 align-middle text-center w-100" id="tbl1"> <table class="table table-sm table-hover table-striped mb-0 align-middle text-center w-100" id="tbl1" >
<thead> <thead>
<tr> <tr>
<th rowspan="2" class="align-middle">날짜</th> <th rowspan="2" class="align-middle">날짜</th>
@@ -121,6 +55,36 @@
<!-- 2) 지역별 --> <!-- 2) 지역별 -->
<div class="tab-pane fade" id="tab-eg10-1" role="tabpanel"> <div class="tab-pane fade" id="tab-eg10-1" role="tabpanel">
<!-- 조회조건 -->
<div class="d-flex flex-wrap align-items-end justify-content-between gap-2 mb-3">
<div class="d-flex flex-wrap align-items-end gap-2">
<label class="form-label mb-1 small me-2">조회조건</label>
<div id="form2" class="d-flex align-items-end gap-1">
<select class="form-select form-select-sm" name="region2" id="region2" style="min-width: 180px;">
<option value="">지역 선택</option>
<?php foreach ($sido as $s): ?>
<option value="<?= $s['region_cd'] ?>" <?php if ($s['region_cd'] == "1100000000") {
echo "selected";
} ?>>
<?= $s['region_nm'] ?>
</option>
<?php endforeach; ?>
</select>
<div class="input-group input-group-sm" style="min-width: 180px;">
<input type="date" class="form-control" name="sdate2" id="sdate2">
</div>
</div>
</div>
<div class="d-flex gap-1 ms-auto">
<button class="btn btn-sm btn-outline-success" id="excel-download" type="button">
<i class="fa fa-fw" aria-hidden="true" title="file-excel-o"></i> 엑셀다운로드
</button>
<button class="btn btn-sm btn-outline-light" type="button" id="btnSearch">
조회
</button>
</div>
</div>
<div class="border rounded p-3 bg-white"> <div class="border rounded p-3 bg-white">
<table class="table table-sm table-hover table-striped mb-0 align-middle text-center w-100" id="tbl2"> <table class="table table-sm table-hover table-striped mb-0 align-middle text-center w-100" id="tbl2">
<thead> <thead>
@@ -142,6 +106,33 @@
<!-- 3) 기본 수량 --> <!-- 3) 기본 수량 -->
<div class="tab-pane fade" id="tab-eg10-2" role="tabpanel"> <div class="tab-pane fade" id="tab-eg10-2" role="tabpanel">
<!-- 조회조건 -->
<div class="d-flex flex-wrap align-items-end justify-content-between gap-2 mb-3">
<div class="d-flex flex-wrap align-items-end gap-2">
<label class="form-label mb-1 small me-2">조회조건</label>
<div id="form3" class="d-flex align-items-end gap-1">
<select class="form-select form-select-sm" name="region3" id="region3" style="min-width: 180px;">
<option value="">지역 선택</option>
<?php foreach ($sido as $s): ?>
<option value="<?= $s['region_cd'] ?>" <?php if ($s['region_cd'] == "1100000000") {
echo "selected";
} ?>>
<?= $s['region_nm'] ?>
</option>
<?php endforeach; ?>
</select>
</div>
</div>
<div class="d-flex gap-1 ms-auto">
<button class="btn btn-sm btn-outline-success" id="excel-download" type="button">
<i class="fa fa-fw" aria-hidden="true" title="file-excel-o"></i> 엑셀다운로드
</button>
<button class="btn btn-sm btn-outline-light" type="button" id="btnSearch">
조회
</button>
</div>
</div>
<div class="border rounded p-3 bg-white"> <div class="border rounded p-3 bg-white">
<table class="table table-sm table-hover table-striped mb-0 align-middle text-center w-100" id="tbl3"> <table class="table table-sm table-hover table-striped mb-0 align-middle text-center w-100" id="tbl3">
<thead> <thead>
@@ -193,9 +184,12 @@
initForm(); initForm();
$("#sdate, #edate").on("change", function () { // DataTable 초기화 후 날짜 변경 이벤트 등록
table1.ajax.reload(); setTimeout(() => {
$("#sdate-dt, #edate-dt").on("change", function () {
if (table1) table1.ajax.reload();
}); });
}, 500);
$("#btnSearch").on("click", function () { $("#btnSearch").on("click", function () {
@@ -216,6 +210,47 @@
language: lang_kor, language: lang_kor,
serverSide: true, serverSide: true,
processing: true, processing: true,
dom: '<"d-flex flex-wrap align-items-end justify-content-between gap-2 mb-2"<"#tbl1-filter">r>tip',
initComplete: function () {
// DataTable 생성 완료 후 필터 영역에 조회조건 삽입
const fmt = d => d.toISOString().slice(0, 10);
const lastDate = getLastDateOfMonth(date.getFullYear(), date.getMonth() + 1);
$('#tbl1-filter').html(`
<div class="d-flex flex-wrap align-items-end gap-2">
<label class="form-label mb-1 small me-2">조회조건</label>
<div class="input-group input-group-sm" style="min-width: 320px;">
<input type="date" class="form-control" id="sdate-dt" value="${fmt(date)}">
<span class="input-group-text">~</span>
<input type="date" class="form-control" id="edate-dt" value="${fmt(lastDate)}">
</div>
</div>
<div class="d-flex gap-1 ms-auto">
<button id="excel-download-dt" class="btn btn-sm btn-outline-success" type="button">
<i class="fa fa-fw fa-file-excel-o"></i> 엑셀다운로드
</button>
<button id="btnSearch1" class="btn btn-sm btn-outline-light" type="button">조회</button>
</div>
`);
// 이벤트 바인딩
$('#sdate-dt, #edate-dt').on('change', function () {
table1.ajax.reload();
});
$('#btnSearch1').on('click', function () {
table1.ajax.reload();
});
$('#excel-download-dt').on('click', function () {
$.ajax({
url: "/article/processible/excel",
method: "GET",
data: { sdate: $("#sdate-dt").val(), edate: $("#edate-dt").val() },
beforeSend: function () { blockUI.blockPage({ message: tpl }) },
complete: function () { blockUI.unblockPage() },
success: function (result) { downloadExcelWithHeader(result.data); }
});
});
},
ajax: { ajax: {
url: '/article/processible/getList1', url: '/article/processible/getList1',
type: 'GET', type: 'GET',
@@ -228,11 +263,15 @@
blockUI.unblockPage() blockUI.unblockPage()
}, },
data: function (d) { data: function (d) {
d.sdate = $("#sdate").val(); // 시작일 const fmt = d2 => d2.toISOString().slice(0, 10);
d.edate = $("#edate").val(); // 종료일 const lastDate = getLastDateOfMonth(date.getFullYear(), date.getMonth() + 1);
d.start = d.start || 0 // input이 DOM에 있으면 그 값, 없으면 오늘/말일 기본값
d.length = d.length || 10 d.sdate = $("#sdate-dt").val() || fmt(date);
d.edate = $("#edate-dt").val() || fmt(lastDate);
d.start = d.start || 0;
d.length = d.length || 10;
}, },
}, },
"columnDefs": [ "columnDefs": [
@@ -357,10 +396,10 @@
const fmt = d => d.toISOString().slice(0, 10); const fmt = d => d.toISOString().slice(0, 10);
$('#sdate').val(fmt(date)); $('#sdate-dt').val(fmt(date));
const lastDate = getLastDateOfMonth(date.getFullYear(), date.getMonth() + 1); const lastDate = getLastDateOfMonth(date.getFullYear(), date.getMonth() + 1);
$('#edate').val(fmt(lastDate)); $('#edate-dt').val(fmt(lastDate));
$('#sdate2').val(fmt(date)); $('#sdate2').val(fmt(date));
} }
@@ -513,7 +552,7 @@
data: null, render: function (data, type, row, meta) { data: null, render: function (data, type, row, meta) {
var str = ` var str = `
<div class="d-flex justify-content-center gap-1"> <div class="d-flex justify-content-center gap-1">
<input type="text" id="am_cnt2__${meta.row}" value="${row.am_cnt}"/ style="width: 80px;"> <input type="text" id="am_cnt2_${meta.row}" value="${row.am_cnt}"/ style="width: 80px;">
</div> </div>
`; `;
@@ -706,8 +745,8 @@
datas.push(data); datas.push(data);
}); });
} }
// console.log(path);
// console.log(datas) console.log(datas)
// return // return
if (datas.length == 0) { if (datas.length == 0) {

File diff suppressed because it is too large Load Diff

View File

@@ -14,6 +14,12 @@ $usr_nm = session('usr_nm');
text-align: center; text-align: center;
} }
/* 테이블 헤더 좌우 여백 조정 */
#resultList thead th {
padding-left: 4px !important;
padding-right: 4px !important;
}
td { td {
text-align: center; text-align: center;
} }
@@ -66,19 +72,47 @@ $usr_nm = session('usr_nm');
overflow-x: auto !important; overflow-x: auto !important;
} }
/* 테이블 폭: 내용 기준으로 커지되, 최소는 100% */ /* 테이블 폭: 내용에 따라 자동 조정, 스크롤 필요시 표시 */
.table-responsive #resultList { .table-responsive #resultList {
width: max-content !important; width: 100% !important;
min-width: 100% !important;
table-layout: auto !important; table-layout: auto !important;
} }
/* 줄바꿈 금지 */ /* 줄바꿈 허용 - 공백 기준으로 줄바꿈 */
#resultList th, #resultList th,
#resultList td { #resultList td {
white-space: nowrap; white-space: normal !important;
word-wrap: break-word;
} }
/* tw-* 클래스가 DataTable의 inline 스타일보다 우선순위 높이기 */
#resultList th.tw-30,
#resultList td.tw-30 { width: 30px !important; max-width: 30px !important; min-width: 30px !important; }
#resultList th.tw-50,
#resultList td.tw-50 { width: 50px !important; max-width: 50px !important; min-width: 50px !important; }
#resultList th.tw-70,
#resultList td.tw-70 { width: 70px !important; max-width: 70px !important; min-width: 70px !important; }
#resultList th.tw-80,
#resultList td.tw-80 { width: 80px !important; max-width: 80px !important; min-width: 80px !important; }
#resultList th.tw-90,
#resultList td.tw-90 { width: 90px !important; max-width: 90px !important; min-width: 90px !important; }
#resultList th.tw-100,
#resultList td.tw-100 { width: 100px !important; max-width: 100px !important; min-width: 100px !important; }
#resultList th.tw-120,
#resultList td.tw-120 { width: 120px !important; max-width: 120px !important; min-width: 120px !important; }
#resultList th.tw-130,
#resultList td.tw-130 { width: 130px !important; max-width: 130px !important; min-width: 130px !important; }
#resultList th.tw-140,
#resultList td.tw-140 { width: 140px !important; max-width: 140px !important; min-width: 140px !important; }
#resultList th.tw-150,
#resultList td.tw-150 { width: 150px !important; max-width: 150px !important; min-width: 150px !important; }
#resultList th.tw-180,
#resultList td.tw-180 { width: 180px !important; max-width: 180px !important; min-width: 180px !important; }
#resultList th.tw-200,
#resultList td.tw-200 { width: 200px !important; max-width: 200px !important; min-width: 200px !important; }
#resultList th.tw-250,
#resultList td.tw-250 { width: 250px !important; max-width: 250px !important; min-width: 250px !important; }
/* PC에서 가로 스크롤이 잘리는 대표 구간들 강제 해제 */ /* PC에서 가로 스크롤이 잘리는 대표 구간들 강제 해제 */
.main-card, .main-card,
.card, .card,
@@ -101,32 +135,47 @@ $usr_nm = session('usr_nm');
.card-body { .card-body {
min-width: 0; min-width: 0;
} }
</style>
<h1>확인매물 현황</h1> /* 검색 영역 행마다 구분선 추가 */
#frm_srch_info .row.g-3 {
padding: 12px 0;
border-bottom: 1px solid #e9ecef;
}
#frm_srch_info .row.g-3:last-child {
border-bottom: none;
}
</style>
<div class="col-md-12 col-xl-12"> <div class="col-md-12 col-xl-12">
<div class="main-card mb-3 card"> <div class="main-card mb-3 card">
<div class="card-header bg-white border-bottom shadow-sm">
<div class="d-flex flex-wrap align-items-center gap-3 card-header-tab">
<div>
<h4 class="mb-0 fw-bold text-dark">확인매물 현황</h4>
</div>
</div>
</div>
<div class="card-body"> <div class="card-body">
<form id="frm_srch_info" method="get" onsubmit="return false;"> <form id="frm_srch_info" method="get" onsubmit="return false;">
<!-- 검색 폼 --> <!-- 검색 폼 -->
<div class="row g-3"> <div class="row g-3">
<div class="col-md-1"> <div class="col-md-1">
<label class="form-label mb-1">매물ID</label> <label class="form-label mb-1">매물ID</label>
<input type="text" class="form-control" name="rcpt_atclno" id="rcpt_atclno" <input type="text" class="form-control form-control-sm" name="rcpt_atclno" id="rcpt_atclno"
onkeypress="atcl_no_enter(event)"> onkeypress="atcl_no_enter(event)">
</div> </div>
<div class="col-md-4"> <div class="col-md-4">
<label class="form-label mb-1">일자별조회</label> <label class="form-label mb-1">일자별조회</label>
<div class="input-group input-group-sm"> <div class="input-group input-group-sm">
<select class="form-select" name="schDateGb"> <select class="form-select form-select-sm" name="schDateGb">
<option value="1" selected>예약일자</option> <option value="1" selected>예약일자</option>
<option value="2">등록일자</option> <option value="2">등록일자</option>
</select> </select>
<input type="date" class="form-control" name="sdate" id="sdate" placeholder="시작일"> <input type="date" class="form-control form-control-sm" name="sdate" id="sdate" placeholder="시작일">
<span class="input-group-text">~</span> <span class="input-group-text">~</span>
<input type="date" class="form-control" name="edate" id="edate" placeholder="종료일"> <input type="date" class="form-control form-control-sm" name="edate" id="edate" placeholder="종료일">
</div> </div>
</div> </div>
@@ -176,10 +225,8 @@ $usr_nm = session('usr_nm');
<select name="rcpt_stat1" class="form-select form-select-sm"> <select name="rcpt_stat1" class="form-select form-select-sm">
<option value="">선택</option> <option value="">선택</option>
<?php foreach ($codes as $c): ?> <?php foreach (($codes['STEP_VERIFICATION']['items'] ?? []) as $cd => $cdNm): ?>
<?php if ($c['category'] === "STEP_VERIFICATION"): ?> <option value="<?= $cd ?>"><?= $cdNm ?></option>
<option value="<?= $c['cd'] ?>"><?= $c['cd_nm'] ?></option>
<?php endif; ?>
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
<!-- <select name="rcpt_stat2" id="srcGugun" class="form-select form-select-sm"> <!-- <select name="rcpt_stat2" id="srcGugun" class="form-select form-select-sm">
@@ -193,19 +240,17 @@ $usr_nm = session('usr_nm');
<div class="col-md-1"> <div class="col-md-1">
<label class="form-label mb-1">거래구분</label> <label class="form-label mb-1">거래구분</label>
<select class="form-select" name="rcpt_product_info1"> <select class="form-select form-select-sm" name="rcpt_product_info1">
<option value="">전체</option> <option value="">전체</option>
<?php foreach ($codes as $c): ?> <?php foreach (($codes['NHN_DEAL_TYPE']['items'] ?? []) as $cd => $cdNm): ?>
<?php if ($c['category'] === "NHN_DEAL_TYPE"): ?> <option value="<?= $cdNm ?>"><?= $cdNm ?></option>
<option value="<?= $c['cd'] ?>"><?= $c['cd_nm'] ?></option>
<?php endif; ?>
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
</div> </div>
<div class="col-md-1"> <div class="col-md-1">
<label class="form-label mb-1">동영상촬영여부</label> <label class="form-label mb-1">동영상촬영여부</label>
<select class="form-select" name="exp_movie_yn"> <select class="form-select form-select-sm" name="exp_movie_yn">
<option value="">전체</option> <option value="">전체</option>
<option value="Y">촬영</option> <option value="Y">촬영</option>
<option value="N">미촬영</option> <option value="N">미촬영</option>
@@ -214,7 +259,7 @@ $usr_nm = session('usr_nm');
<div class="col-md-1"> <div class="col-md-1">
<label class="form-label mb-1">홍보확인서여부</label> <label class="form-label mb-1">홍보확인서여부</label>
<select class="form-select" name="conf_img_yn"> <select class="form-select form-select-sm" name="conf_img_yn">
<option value="">전체</option> <option value="">전체</option>
<option value="Y">Y</option> <option value="Y">Y</option>
<option value="N">N</option> <option value="N">N</option>
@@ -223,7 +268,7 @@ $usr_nm = session('usr_nm');
<div class="col-md-1"> <div class="col-md-1">
<label class="form-label mb-1">분양권</label> <label class="form-label mb-1">분양권</label>
<select class="form-select" name="parcel_out_yn"> <select class="form-select form-select-sm" name="parcel_out_yn">
<option value="">전체</option> <option value="">전체</option>
<option value="Y"> Y</option> <option value="Y"> Y</option>
<option value="N"> N</option> <option value="N"> N</option>
@@ -232,31 +277,27 @@ $usr_nm = session('usr_nm');
<div class="col-md-1"> <div class="col-md-1">
<label class="form-label mb-1">CP ID</label> <label class="form-label mb-1">CP ID</label>
<select class="form-select" name="rcpt_cpid"> <select class="form-select form-select-sm" name="rcpt_cpid">
<option value="">전체</option> <option value="">전체</option>
<?php foreach ($codes as $c): ?> <?php foreach (($codes['CP_ID']['items'] ?? []) as $cd => $cdNm): ?>
<?php if ($c['category'] === "CP_ID"): ?> <option value="<?= $cd ?>"><?= $cdNm ?></option>
<option value="<?= $c['cd'] ?>"><?= $c['cd_nm'] ?></option>
<?php endif; ?>
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
</div> </div>
<div class="col-md-1"> <div class="col-md-1">
<label class="form-label mb-1">매물종류</label> <label class="form-label mb-1">매물종류</label>
<select class="form-select" name="rcpt_product"> <select class="form-select form-select-sm" name="rcpt_product">
<option value="">전체</option> <option value="">전체</option>
<?php foreach ($codes as $c): ?> <?php foreach (($codes['ARTICLE_TYPE']['items'] ?? []) as $cd => $cdNm): ?>
<?php if ($c['category'] === "ARTICLE_TYPE"): ?> <option value="<?= $cd ?>"><?= $cdNm ?></option>
<option value="<?= $c['cd'] ?>"><?= $c['cd_nm'] ?></option>
<?php endif; ?>
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
</div> </div>
<div class="col-md-1"> <div class="col-md-1">
<label class="form-label mb-1">면적확인</label> <label class="form-label mb-1">면적확인</label>
<select class="form-select" name="exp_spc_yn"> <select class="form-select form-select-sm" name="exp_spc_yn">
<option value="">전체</option> <option value="">전체</option>
<option value="Y"> Y</option> <option value="Y"> Y</option>
<option value="N"> N</option> <option value="N"> N</option>
@@ -265,7 +306,7 @@ $usr_nm = session('usr_nm');
<div class="col-md-1"> <div class="col-md-1">
<label class="form-label mb-1">체크리스트</label> <label class="form-label mb-1">체크리스트</label>
<select class="form-select" name="check_list_img_yn"> <select class="form-select form-select-sm" name="check_list_img_yn">
<option value="">전체</option> <option value="">전체</option>
<option value="Y"> Y <option value="Y"> Y
</option> </option>
@@ -276,7 +317,7 @@ $usr_nm = session('usr_nm');
<div class="col-md-1"> <div class="col-md-1">
<label class="form-label mb-1">평면도유무</label> <label class="form-label mb-1">평면도유무</label>
<select class="form-select" name="ground_plan_yn"> <select class="form-select form-select-sm" name="ground_plan_yn">
<option value="">전체</option> <option value="">전체</option>
<option value="Y">Y</option> <option value="Y">Y</option>
<option value="N">N</option> <option value="N">N</option>
@@ -285,7 +326,7 @@ $usr_nm = session('usr_nm');
<div class="col-md-1"> <div class="col-md-1">
<label class="form-label mb-1">평면도요청</label> <label class="form-label mb-1">평면도요청</label>
<select class="form-select" name="ground_plan"> <select class="form-select form-select-sm" name="ground_plan">
<option value="">전체</option> <option value="">전체</option>
<option value="Y">Y</option> <option value="Y">Y</option>
<option value="N">N</option> <option value="N">N</option>
@@ -294,7 +335,7 @@ $usr_nm = session('usr_nm');
<div class="col-md-1"> <div class="col-md-1">
<label class="form-label mb-1">직거래</label> <label class="form-label mb-1">직거래</label>
<select class="form-select" name="direct_trad_yn"> <select class="form-select form-select-sm" name="direct_trad_yn">
<option value="">전체</option> <option value="">전체</option>
<option value="Y">Y</option> <option value="Y">Y</option>
<option value="N">N</option> <option value="N">N</option>
@@ -303,7 +344,7 @@ $usr_nm = session('usr_nm');
<div class="col-md-1"> <div class="col-md-1">
<label class="form-label mb-1">360촬영여부</label> <label class="form-label mb-1">360촬영여부</label>
<select class="form-select" name="image_360_yn"> <select class="form-select form-select-sm" name="image_360_yn">
<option value="">전체</option> <option value="">전체</option>
<option value="Y">Y</option> <option value="Y">Y</option>
<option value="N">N</option> <option value="N">N</option>
@@ -313,7 +354,7 @@ $usr_nm = session('usr_nm');
<!-- 검색유형 --> <!-- 검색유형 -->
<div class="col-md-1"> <div class="col-md-1">
<label class="form-label mb-1">검색유형</label> <label class="form-label mb-1">검색유형</label>
<select class="form-select" name="srchType"> <select class="form-select form-select-sm" name="srchType">
<option value="">선택</option> <option value="">선택</option>
<option value="1">중개사명</option> <option value="1">중개사명</option>
<option value="2">주소</option> <option value="2">주소</option>
@@ -324,7 +365,7 @@ $usr_nm = session('usr_nm');
<!-- 검색어 --> <!-- 검색어 -->
<div class="col-md-2"> <div class="col-md-2">
<label class="form-label mb-1">검색어</label> <label class="form-label mb-1">검색어</label>
<input type="text" class="form-control" name="srchTxt" placeholder="검색어 입력"> <input type="text" class="form-control form-control-sm" name="srchTxt" placeholder="검색어 입력">
</div> </div>
<div class="col-md-1 d-grid"> <div class="col-md-1 d-grid">
@@ -341,18 +382,17 @@ $usr_nm = session('usr_nm');
</div> </div>
<div class="main-card mb-3 card"> <div class="main-card mb-3 card">
<div class="card-header d-flex align-items-center"> <div class="card-header bg-white border-bottom shadow-sm">
<div class="d-flex align-items-center flex-wrap" style="gap: 8px; flex: 1"> <div class="d-flex flex-wrap align-items-center w-100 justify-content-between card-header-tab">
<h5 class="mb-0 fw-bold text-dark">검색 결과</h5>
</div> <div class="d-flex align-items-center gap-2 ms-auto">
<div class="ml-auto">
<button class="btn btn-sm btn-outline-success" id="excel-download"> <button class="btn btn-sm btn-outline-success" id="excel-download">
<i class="fa fa-fw" aria-hidden="true" title="file-excel-o"></i> <i class="fa fa-fw" aria-hidden="true" title="file-excel-o"></i>
엑셀다운로드 엑셀다운로드
</button> </button>
</div> </div>
</div> </div>
</div>
<div class="card-body"> <div class="card-body">
<div class="table-responsive"> <div class="table-responsive">
<table id="resultList" class="table table-hover table-striped table-bordered"> <table id="resultList" class="table table-hover table-striped table-bordered">
@@ -404,13 +444,12 @@ $usr_nm = session('usr_nm');
const bonbuArr = <?= json_encode($bonbu, JSON_UNESCAPED_UNICODE); ?>; const bonbuArr = <?= json_encode($bonbu, JSON_UNESCAPED_UNICODE); ?>;
const teamArr = <?= json_encode($team, JSON_UNESCAPED_UNICODE); ?>; const teamArr = <?= json_encode($team, JSON_UNESCAPED_UNICODE); ?>;
const userArr = <?= json_encode($user, JSON_UNESCAPED_UNICODE); ?>; const userArr = <?= json_encode($user, JSON_UNESCAPED_UNICODE); ?>;
<?php if (isset($srchUser) && !empty($srchUser)): ?> const srchUser = <?= json_encode((!empty($srchUser) ? $srchUser : null), JSON_UNESCAPED_UNICODE); ?>;
const srchUser = <?= json_encode($srchUser, JSON_UNESCAPED_UNICODE); ?>;
<?php else: ?>
const srchUser = null;
<?php endif; ?>
const sBonbu = "<?= $sBonbu ?? '' ?>"; const sBonbu = "<?= $sBonbu ?? '' ?>";
const sTeam = "<?= $sTeanm ?? '' ?>"; const sTeam = "<?= $sTeam ?? '' ?>";
const userColumns = <?= ($usr_level != '45')
? "[{ data: 'dept_nm' }, { data: 'usr_nm' }]"
: "[]" ?>;
const date = new Date(); const date = new Date();
var table; var table;
@@ -588,6 +627,7 @@ $usr_nm = session('usr_nm');
language: lang_kor, language: lang_kor,
serverSide: true, serverSide: true,
processing: true, processing: true,
autoWidth: false,
ajax: { ajax: {
url: '/article/receipt/getResultList', url: '/article/receipt/getResultList',
type: 'GET', type: 'GET',
@@ -646,27 +686,23 @@ $usr_nm = session('usr_nm');
columns: [ columns: [
{ data: 'rcpt_stat_nm' }, { data: 'rcpt_stat_nm' },
{ data: 'rcpt_atclno' }, { data: 'rcpt_atclno' },
{ data: 'insert_tm' }, { data: 'insert_tm', className: 'tw-80' },
{ data: null, render: fn_rsrv_render }, { data: null, render: fn_rsrv_render },
{ data: 'rsrv_tm_hour' }, { data: 'rsrv_tm_hour' },
{ data: 'photo_save_dt' }, { data: 'photo_save_dt' },
{ data: 'rcpt_cpid' }, { data: 'rcpt_cpid' },
{ data: null, render: fn_agent_render }, { data: null, render: fn_agent_render , className: 'tw-150' },
{ data: null, render: fn_addr_render }, { data: null, render: fn_addr_render },
{ data: null, render: fn_prd_render }, { data: null, render: fn_prd_render },
{ data: 'rcpt_product_info1' }, { data: 'rcpt_product_info1' }
<?php if ($usr_level != "45"): ?> ].concat(userColumns, [
{ data: 'dept_nm' },
{ data: 'usr_nm' },
<?php endif; ?>
{ data: 'parcel_out_yn' }, { data: 'parcel_out_yn' },
{ data: 'conf_img_yn' }, { data: 'conf_img_yn' },
{ data: 'exp_movie_yn' }, { data: 'exp_movie_yn' },
{ data: 'ground_plan_yn' }, { data: 'ground_plan_yn' },
{ data: 'ground_plan' }, { data: 'ground_plan' },
{ data: 'exp_spc_yn' }, { data: 'exp_spc_yn' }
]),
],
// 옵션들 예시 // 옵션들 예시
destroy: true, destroy: true,
deferRender: true, deferRender: true,
@@ -685,7 +721,7 @@ $usr_nm = session('usr_nm');
if (!rowData) return; if (!rowData) return;
const rcpt_atclno = rowData.rcpt_atclno; const rcpt_atclno = rowData.rcpt_atclno;
window.open("<?= site_url('article/receipt/detail') ?>/" + rcpt_atclno, '_blank'); window.location.href = "<?= site_url('article/receipt/detail') ?>/" + rcpt_atclno;
}); });
@@ -831,14 +867,16 @@ $usr_nm = session('usr_nm');
function fn_addr_render(data, type, row) { function fn_addr_render(data, type, row) {
var str = ""; var str = "";
var addr = row.addr || '';
var lineBreak = addr ? "<br/>" : "";
if (row.rcpt_jibun_addr == null || row.rcpt_jibun_addr == "") { if (row.rcpt_jibun_addr == null || row.rcpt_jibun_addr == "") {
str = row.addr + "<br/>" + row.rcpt_hscp_nm + " " + row.rcpt_dtl_addr + " " + row.rcpt_ho; str = addr + lineBreak + (row.rcpt_hscp_nm || '') + " " + (row.rcpt_dtl_addr || '') + " " + (row.rcpt_ho || '');
} else { } else {
str = row.addr + "<br/>" + row.rcpt_hscp_nm + " " + row.rcpt_li_addr + " " + row.rcpt_jibun_addr + " " + row.rcpt_etc_addr; str = addr + lineBreak + (row.rcpt_hscp_nm || '') + " " + (row.rcpt_li_addr || '') + " " + (row.rcpt_jibun_addr || '') + " " + (row.rcpt_etc_addr || '');
} }
return str; return str.trim();
} }
function fn_prd_render(data, type, row) { function fn_prd_render(data, type, row) {

View File

@@ -14,6 +14,12 @@ $usr_nm = session('usr_nm');
text-align: center; text-align: center;
} }
/* 테이블 헤더 좌우 여백 조정 */
#resultList thead th {
padding-left: 4px !important;
padding-right: 4px !important;
}
td { td {
text-align: center; text-align: center;
} }
@@ -66,19 +72,47 @@ $usr_nm = session('usr_nm');
overflow-x: auto !important; overflow-x: auto !important;
} }
/* 테이블 폭: 내용 기준으로 커지되, 최소는 100% */ /* 테이블 폭: 내용에 따라 자동 조정, 스크롤 필요시 표시 */
.table-responsive #resultList { .table-responsive #resultList {
width: max-content !important; width: 100% !important;
min-width: 100% !important;
table-layout: auto !important; table-layout: auto !important;
} }
/* 줄바꿈 금지 */ /* 줄바꿈 허용 - 공백 기준으로 줄바꿈 */
#resultList th, #resultList th,
#resultList td { #resultList td {
white-space: nowrap; white-space: normal !important;
word-wrap: break-word;
} }
/* tw-* 클래스가 DataTable의 inline 스타일보다 우선순위 높이기 */
#resultList th.tw-30,
#resultList td.tw-30 { width: 30px !important; max-width: 30px !important; min-width: 30px !important; }
#resultList th.tw-50,
#resultList td.tw-50 { width: 50px !important; max-width: 50px !important; min-width: 50px !important; }
#resultList th.tw-70,
#resultList td.tw-70 { width: 70px !important; max-width: 70px !important; min-width: 70px !important; }
#resultList th.tw-80,
#resultList td.tw-80 { width: 80px !important; max-width: 80px !important; min-width: 80px !important; }
#resultList th.tw-90,
#resultList td.tw-90 { width: 90px !important; max-width: 90px !important; min-width: 90px !important; }
#resultList th.tw-100,
#resultList td.tw-100 { width: 100px !important; max-width: 100px !important; min-width: 100px !important; }
#resultList th.tw-120,
#resultList td.tw-120 { width: 120px !important; max-width: 120px !important; min-width: 120px !important; }
#resultList th.tw-130,
#resultList td.tw-130 { width: 130px !important; max-width: 130px !important; min-width: 130px !important; }
#resultList th.tw-140,
#resultList td.tw-140 { width: 140px !important; max-width: 140px !important; min-width: 140px !important; }
#resultList th.tw-150,
#resultList td.tw-150 { width: 150px !important; max-width: 150px !important; min-width: 150px !important; }
#resultList th.tw-180,
#resultList td.tw-180 { width: 180px !important; max-width: 180px !important; min-width: 180px !important; }
#resultList th.tw-200,
#resultList td.tw-200 { width: 200px !important; max-width: 200px !important; min-width: 200px !important; }
#resultList th.tw-250,
#resultList td.tw-250 { width: 250px !important; max-width: 250px !important; min-width: 250px !important; }
/* PC에서 가로 스크롤이 잘리는 대표 구간들 강제 해제 */ /* PC에서 가로 스크롤이 잘리는 대표 구간들 강제 해제 */
.main-card, .main-card,
.card, .card,
@@ -101,12 +135,27 @@ $usr_nm = session('usr_nm');
.card-body { .card-body {
min-width: 0; min-width: 0;
} }
</style>
<h1>현장확인V2 매물 접수 현황</h1> /* 검색 영역 행마다 구분선 추가 */
#frm_srch_info .row.g-3 {
padding: 12px 0;
border-bottom: 1px solid #e9ecef;
}
#frm_srch_info .row.g-3:last-child {
border-bottom: none;
}
</style>
<div class="col-md-12 col-xl-12"> <div class="col-md-12 col-xl-12">
<div class="main-card mb-3 card"> <div class="main-card mb-3 card">
<div class="card-header bg-white border-bottom shadow-sm">
<div class="d-flex flex-wrap align-items-center gap-3 card-header-tab">
<div>
<h4 class="mb-0 fw-bold text-dark">현장확인V2 매물 접수 현황</h4>
</div>
</div>
</div>
<div class="card-body"> <div class="card-body">
<form id="frm_srch_info" method="get" onsubmit="return false;"> <form id="frm_srch_info" method="get" onsubmit="return false;">
@@ -176,10 +225,8 @@ $usr_nm = session('usr_nm');
<div class="d-flex gap-1"> <div class="d-flex gap-1">
<select name="rcpt_stat1" class="form-select form-select-sm"> <select name="rcpt_stat1" class="form-select form-select-sm">
<option value="">예약확인지연</option> <option value="">예약확인지연</option>
<?php foreach ($codes as $c): ?> <?php foreach (($codes['STEP_VERIFICATION']['items'] ?? []) as $cd => $cdNm): ?>
<?php if ($c['category'] === "STEP_VERIFICATION"): ?> <option value="<?= $cd ?>"><?= $cdNm ?></option>
<option value="<?= $c['cd'] ?>"><?= $c['cd_nm'] ?></option>
<?php endif; ?>
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
<select name="rcpt_stat2" id="srcGugun" class="form-select form-select-sm"> <select name="rcpt_stat2" id="srcGugun" class="form-select form-select-sm">
@@ -195,10 +242,8 @@ $usr_nm = session('usr_nm');
<label class="form-label mb-1">거래구분</label> <label class="form-label mb-1">거래구분</label>
<select class="form-select" name="rcpt_product_info1"> <select class="form-select" name="rcpt_product_info1">
<option value="">전체</option> <option value="">전체</option>
<?php foreach ($codes as $c): ?> <?php foreach (($codes['NHN_DEAL_TYPE']['items'] ?? []) as $cd => $cdNm): ?>
<?php if ($c['category'] === "NHN_DEAL_TYPE"): ?> <option value="<?= $cdNm ?>"><?= $cdNm ?></option>
<option value="<?= $c['cd'] ?>"><?= $c['cd_nm'] ?></option>
<?php endif; ?>
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
</div> </div>
@@ -234,10 +279,8 @@ $usr_nm = session('usr_nm');
<label class="form-label mb-1">CP ID</label> <label class="form-label mb-1">CP ID</label>
<select class="form-select" name="rcpt_cpid"> <select class="form-select" name="rcpt_cpid">
<option value="">전체</option> <option value="">전체</option>
<?php foreach ($codes as $c): ?> <?php foreach (($codes['CP_ID']['items'] ?? []) as $cd => $cdNm): ?>
<?php if ($c['category'] === "CP_ID"): ?> <option value="<?= $cd ?>"><?= $cdNm ?></option>
<option value="<?= $c['cd'] ?>"><?= $c['cd_nm'] ?></option>
<?php endif; ?>
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
</div> </div>
@@ -246,10 +289,8 @@ $usr_nm = session('usr_nm');
<label class="form-label mb-1">매물종류</label> <label class="form-label mb-1">매물종류</label>
<select class="form-select" name="rcpt_product"> <select class="form-select" name="rcpt_product">
<option value="">전체</option> <option value="">전체</option>
<?php foreach ($codes as $c): ?> <?php foreach (($codes['ARTICLE_TYPE']['items'] ?? []) as $cd => $cdNm): ?>
<?php if ($c['category'] === "ARTICLE_TYPE"): ?> <option value="<?= $cd ?>"><?= $cdNm ?></option>
<option value="<?= $c['cd'] ?>"><?= $c['cd_nm'] ?></option>
<?php endif; ?>
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
</div> </div>
@@ -420,6 +461,9 @@ $usr_nm = session('usr_nm');
const bonbuArr = <?= json_encode($bonbu, JSON_UNESCAPED_UNICODE); ?>; const bonbuArr = <?= json_encode($bonbu, JSON_UNESCAPED_UNICODE); ?>;
const teamArr = <?= json_encode($team, JSON_UNESCAPED_UNICODE); ?>; const teamArr = <?= json_encode($team, JSON_UNESCAPED_UNICODE); ?>;
const userArr = <?= json_encode($user, JSON_UNESCAPED_UNICODE); ?>; const userArr = <?= json_encode($user, JSON_UNESCAPED_UNICODE); ?>;
const userColumns = <?= ($usr_level != '45')
? "[{ data: 'dept_nm' }, { data: 'usr_nm' }]"
: "[]" ?>;
const date = new Date(); const date = new Date();
var table; var table;
@@ -660,14 +704,11 @@ $usr_nm = session('usr_nm');
{ data: null, render: fn_agent_render }, { data: null, render: fn_agent_render },
{ data: null, render: fn_addr_render }, { data: null, render: fn_addr_render },
{ data: null, render: fn_prd_render }, { data: null, render: fn_prd_render },
{ data: 'rcpt_product_info1' }, { data: 'rcpt_product_info1' }
<?php if ($usr_level != "45"): ?> ].concat(userColumns, [
{ data: 'dept_nm' },
{ data: 'usr_nm' },
<?php endif; ?>
{ data: 'parcel_out_yn' }, { data: 'parcel_out_yn' },
{ data: 'conf_img_yn' }, { data: 'conf_img_yn' }
], ]),
// 옵션들 예시 // 옵션들 예시
destroy: true, destroy: true,
deferRender: true, deferRender: true,

View File

@@ -2277,6 +2277,7 @@ $usr_level = session('usr_level');
} }
} }
// 가격수정 저장 // 가격수정 저장
function modifyPriceInfo() { function modifyPriceInfo() {
swal.fire({ swal.fire({
@@ -2675,9 +2676,10 @@ $usr_level = session('usr_level');
title: "정상 처리되었습니다.", title: "정상 처리되었습니다.",
icon: "success", icon: "success",
draggable: true draggable: true
}) }).then(() => {
// 녹취파일 체크박스 활성화 및 체크
location.reload(); $('#chk_record').prop('disabled', false).prop('checked', true);
});
} else { } else {
Swal.fire({ Swal.fire({
title: result.msg, title: result.msg,

View File

@@ -14,6 +14,12 @@ $usr_nm = session('usr_nm');
text-align: center; text-align: center;
} }
/* 테이블 헤더 좌우 여백 조정 */
#resultList thead th {
padding-left: 4px !important;
padding-right: 4px !important;
}
td { td {
text-align: center; text-align: center;
} }
@@ -66,19 +72,47 @@ $usr_nm = session('usr_nm');
overflow-x: auto !important; overflow-x: auto !important;
} }
/* 테이블 폭: 내용 기준으로 커지되, 최소는 100% */ /* 테이블 폭: 내용에 따라 자동 조정, 스크롤 필요시 표시 */
.table-responsive #resultList { .table-responsive #resultList {
width: max-content !important; width: 100% !important;
min-width: 100% !important;
table-layout: auto !important; table-layout: auto !important;
} }
/* 줄바꿈 금지 */ /* 줄바꿈 허용 - 공백 기준으로 줄바꿈 */
#resultList th, #resultList th,
#resultList td { #resultList td {
white-space: nowrap; white-space: normal !important;
word-wrap: break-word;
} }
/* tw-* 클래스가 DataTable의 inline 스타일보다 우선순위 높이기 */
#resultList th.tw-30,
#resultList td.tw-30 { width: 30px !important; max-width: 30px !important; min-width: 30px !important; }
#resultList th.tw-50,
#resultList td.tw-50 { width: 50px !important; max-width: 50px !important; min-width: 50px !important; }
#resultList th.tw-70,
#resultList td.tw-70 { width: 70px !important; max-width: 70px !important; min-width: 70px !important; }
#resultList th.tw-80,
#resultList td.tw-80 { width: 80px !important; max-width: 80px !important; min-width: 80px !important; }
#resultList th.tw-90,
#resultList td.tw-90 { width: 90px !important; max-width: 90px !important; min-width: 90px !important; }
#resultList th.tw-100,
#resultList td.tw-100 { width: 100px !important; max-width: 100px !important; min-width: 100px !important; }
#resultList th.tw-120,
#resultList td.tw-120 { width: 120px !important; max-width: 120px !important; min-width: 120px !important; }
#resultList th.tw-130,
#resultList td.tw-130 { width: 130px !important; max-width: 130px !important; min-width: 130px !important; }
#resultList th.tw-140,
#resultList td.tw-140 { width: 140px !important; max-width: 140px !important; min-width: 140px !important; }
#resultList th.tw-150,
#resultList td.tw-150 { width: 150px !important; max-width: 150px !important; min-width: 150px !important; }
#resultList th.tw-180,
#resultList td.tw-180 { width: 180px !important; max-width: 180px !important; min-width: 180px !important; }
#resultList th.tw-200,
#resultList td.tw-200 { width: 200px !important; max-width: 200px !important; min-width: 200px !important; }
#resultList th.tw-250,
#resultList td.tw-250 { width: 250px !important; max-width: 250px !important; min-width: 250px !important; }
/* PC에서 가로 스크롤이 잘리는 대표 구간들 강제 해제 */ /* PC에서 가로 스크롤이 잘리는 대표 구간들 강제 해제 */
.main-card, .main-card,
.card, .card,
@@ -101,12 +135,27 @@ $usr_nm = session('usr_nm');
.card-body { .card-body {
min-width: 0; min-width: 0;
} }
</style>
<h1>녹취매물 내역</h1> /* 검색 영역 행마다 구분선 추가 */
#frm_srch_info .row.g-3 {
padding: 12px 0;
border-bottom: 1px solid #e9ecef;
}
#frm_srch_info .row.g-3:last-child {
border-bottom: none;
}
</style>
<div class="col-md-12 col-xl-12"> <div class="col-md-12 col-xl-12">
<div class="main-card mb-3 card"> <div class="main-card mb-3 card">
<div class="card-header bg-white border-bottom shadow-sm">
<div class="d-flex flex-wrap align-items-center gap-3 card-header-tab">
<div>
<h4 class="mb-0 fw-bold text-dark">녹취매물 내역</h4>
</div>
</div>
</div>
<div class="card-body"> <div class="card-body">
<form id="frm_srch_info" method="get" onsubmit="return false;"> <form id="frm_srch_info" method="get" onsubmit="return false;">
@@ -227,18 +276,17 @@ $usr_nm = session('usr_nm');
</div> </div>
<div class="main-card mb-3 card"> <div class="main-card mb-3 card">
<div class="card-header d-flex align-items-center"> <div class="card-header bg-white border-bottom shadow-sm">
<div class="d-flex align-items-center flex-wrap" style="gap: 8px; flex: 1"> <div class="d-flex flex-wrap align-items-center w-100 justify-content-between card-header-tab">
<h5 class="mb-0 fw-bold text-dark">검색 결과</h5>
</div> <div class="d-flex align-items-center gap-2 ms-auto">
<div class="ml-auto">
<button class="btn btn-sm btn-outline-success" id="excel-download"> <button class="btn btn-sm btn-outline-success" id="excel-download">
<i class="fa fa-fw" aria-hidden="true" title="file-excel-o"></i> <i class="fa fa-fw" aria-hidden="true" title="file-excel-o"></i>
엑셀다운로드 엑셀다운로드
</button> </button>
</div> </div>
</div> </div>
</div>
<div class="card-body"> <div class="card-body">
<div class="table-responsive"> <div class="table-responsive">
<table id="resultList" class="table table-hover table-striped table-bordered"> <table id="resultList" class="table table-hover table-striped table-bordered">

View File

@@ -69,6 +69,15 @@
<div class="h-100 bg-plum-plate bg-animation"> <div class="h-100 bg-plum-plate bg-animation">
<div class="d-flex h-100 justify-content-center align-items-center py-4"> <div class="d-flex h-100 justify-content-center align-items-center py-4">
<div class="mx-auto col-sm-10 col-md-8 col-lg-6 col-xl-5"> <div class="mx-auto col-sm-10 col-md-8 col-lg-6 col-xl-5">
<!-- Redis 장애 경고 영역 -->
<div id="redis-warning" class="alert alert-warning alert-dismissible fade d-none mb-3"
style="border-radius: 16px; background: rgba(255, 193, 7, 0.95); backdrop-filter: blur(10px); box-shadow: 0 4px 12px rgba(0,0,0,0.15);">
<strong><i class="fa fa-exclamation-triangle me-2"></i>시스템 알림</strong>
<p id="redis-warning-message" class="mb-0 mt-2"></p>
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
<div class="card border-0" <div class="card border-0"
style="border-radius: 24px; background: rgba(255, 255, 255, 0.95); backdrop-filter: blur(20px); box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);"> style="border-radius: 24px; background: rgba(255, 255, 255, 0.95); backdrop-filter: blur(20px); box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);">
<div class="card-body p-5"> <div class="card-body p-5">
@@ -202,6 +211,27 @@
const tpl = document.querySelector('.my-loader-template'); const tpl = document.querySelector('.my-loader-template');
var table; var table;
// Redis 장애 경고 표시 함수
function showRedisWarning(message) {
const warningDiv = $('#redis-warning');
const messageEl = $('#redis-warning-message');
messageEl.text(message);
warningDiv.removeClass('d-none').addClass('show');
// 10초 후 자동 숨김 (선택사항)
setTimeout(function() {
warningDiv.fadeOut();
}, 15000);
}
// 시스템 상태 체크 함수
function checkSystemStatus(responseData) {
if (responseData.system && responseData.system.redis_fallback) {
showRedisWarning(responseData.system.warning_message);
}
}
$(function () { $(function () {
$("#btnSearch").on("click", function () { $("#btnSearch").on("click", function () {
@@ -231,6 +261,9 @@
console.log(xhr.responseText); console.log(xhr.responseText);
}, },
success: function (result) { success: function (result) {
// 시스템 상태 체크 (Redis 장애 여부)
checkSystemStatus(result);
if (result.code === "0") { if (result.code === "0") {
location.href = '/' location.href = '/'
} else { } else {

View File

@@ -487,7 +487,7 @@
}) })
}, },
success: function (result) { success: function (result) {
blockUI.unblockPage()
if (result.code == '0') { if (result.code == '0') {
$("#btnSearch").trigger('click') $("#btnSearch").trigger('click')
Swal.fire({ Swal.fire({
@@ -589,7 +589,7 @@
}) })
}, },
success: function (result) { success: function (result) {
blockUI.unblockPage()
if (result.code == '0') { if (result.code == '0') {
regionModalHide() regionModalHide()
$("#btnSearch").trigger('click') $("#btnSearch").trigger('click')

View File

@@ -0,0 +1,284 @@
<?= $this->extend('layouts/main') ?>
<?= $this->section('content') ?>
<div class="app-page-title">
<div class="page-title-wrapper">
<div class="page-title-heading">
<div class="page-title-icon">
<i class="pe-7s-monitor icon-gradient bg-mean-fruit"></i>
</div>
<div>
Worker 로그 통합 관리
<div class="page-title-subheading">
API Receiver 및 Worker 서비스의 로그를 한 곳에서 확인할 수 있습니다.
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="main-card mb-3 card">
<div class="card-header">
<div class="btn-actions-pane-left">
<div class="nav">
<button class="btn btn-sm btn-primary" onclick="refreshLogs()">
<i class="fa fa-sync"></i> 새로고침
</button>
<button class="btn btn-sm btn-success" id="autoRefreshBtn" onclick="toggleAutoRefresh()">
<i class="fa fa-play"></i> 자동 새로고침 시작
</button>
</div>
</div>
<div class="btn-actions-pane-right">
<button class="btn btn-sm btn-info" onclick="downloadLog()">
<i class="fa fa-download"></i> 로그 다운로드
</button>
</div>
</div>
<div class="card-body">
<!-- 필터 영역 -->
<div class="row mb-3">
<div class="col-md-4">
<label>날짜 선택</label>
<select class="form-control" id="dateSelect" onchange="changeDate()">
<?php foreach ($availableDates as $availDate): ?>
<option value="<?= $availDate ?>" <?= $availDate === $date ? 'selected' : '' ?>>
<?= $availDate ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-4">
<label>로그 타입</label>
<select class="form-control" id="typeSelect" onchange="changeType()">
<option value="all" <?= $logType === 'all' ? 'selected' : '' ?>>전체</option>
<option value="api_receiver" <?= $logType === 'api_receiver' ? 'selected' : '' ?>>API Receiver</option>
<option value="naver_worker" <?= $logType === 'naver_worker' ? 'selected' : '' ?>>Naver Worker</option>
</select>
</div>
<div class="col-md-4">
<label>레벨 필터</label>
<select class="form-control" id="levelFilter" onchange="filterByLevel()">
<option value="all">전체</option>
<option value="ERROR">ERROR</option>
<option value="INFO">INFO</option>
<option value="DEBUG">DEBUG</option>
</select>
</div>
</div>
<!-- 통계 영역 -->
<div class="row mb-3">
<div class="col-md-3">
<div class="card mb-3 widget-content bg-midnight-bloom">
<div class="widget-content-wrapper text-white">
<div class="widget-content-left">
<div class="widget-heading">전체 로그</div>
<div class="widget-subheading">Total Logs</div>
</div>
<div class="widget-content-right">
<div class="widget-numbers text-white">
<span id="totalCount"><?= count($logs) ?></span>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card mb-3 widget-content bg-arielle-smile">
<div class="widget-content-wrapper text-white">
<div class="widget-content-left">
<div class="widget-heading">INFO</div>
<div class="widget-subheading">Information</div>
</div>
<div class="widget-content-right">
<div class="widget-numbers text-white">
<span id="infoCount"><?= count(array_filter($logs, fn($l) => $l['level'] === 'INFO')) ?></span>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card mb-3 widget-content bg-grow-early">
<div class="widget-content-wrapper text-white">
<div class="widget-content-left">
<div class="widget-heading">ERROR</div>
<div class="widget-subheading">Errors</div>
</div>
<div class="widget-content-right">
<div class="widget-numbers text-white">
<span id="errorCount"><?= count(array_filter($logs, fn($l) => $l['level'] === 'ERROR')) ?></span>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card mb-3 widget-content bg-premium-dark">
<div class="widget-content-wrapper text-white">
<div class="widget-content-left">
<div class="widget-heading">DEBUG</div>
<div class="widget-subheading">Debug Logs</div>
</div>
<div class="widget-content-right">
<div class="widget-numbers text-white">
<span id="debugCount"><?= count(array_filter($logs, fn($l) => $l['level'] === 'DEBUG')) ?></span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 로그 테이블 -->
<div class="table-responsive" style="max-height: 600px; overflow-y: auto;">
<table class="table table-hover table-striped table-sm" id="logTable">
<thead class="thead-dark" style="position: sticky; top: 0; z-index: 10;">
<tr>
<th style="width: 150px;">시간</th>
<th style="width: 80px;">레벨</th>
<th style="width: 120px;">소스</th>
<th style="width: 150px;">위치</th>
<th>메시지</th>
</tr>
</thead>
<tbody id="logTableBody">
<?php if (empty($logs)): ?>
<tr>
<td colspan="5" class="text-center">로그가 없습니다.</td>
</tr>
<?php else: ?>
<?php foreach ($logs as $log): ?>
<tr class="log-row" data-level="<?= esc($log['level']) ?>">
<td><small><?= esc($log['timestamp']) ?></small></td>
<td>
<span class="badge badge-<?= $log['level'] === 'ERROR' ? 'danger' : ($log['level'] === 'INFO' ? 'success' : 'secondary') ?>">
<?= esc($log['level']) ?>
</span>
</td>
<td>
<span class="badge badge-<?= strpos($log['source'], 'failed') !== false ? 'warning' : 'info' ?>">
<?php
$sourceLabel = [
'api_receiver' => 'API Receiver',
'naver_worker' => 'Worker',
'naver_worker_failed' => 'Worker (Failed)'
];
echo $sourceLabel[$log['source']] ?? $log['source'];
?>
</span>
</td>
<td><small><?= esc($log['location']) ?></small></td>
<td>
<small style="word-break: break-all;">
<?= esc($log['message']) ?>
</small>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<script>
let autoRefreshInterval = null;
let isAutoRefresh = false;
function changeDate() {
const date = document.getElementById('dateSelect').value;
const type = document.getElementById('typeSelect').value;
window.location.href = '<?= base_url('manage/workerlog') ?>?date=' + date + '&type=' + type;
}
function changeType() {
const date = document.getElementById('dateSelect').value;
const type = document.getElementById('typeSelect').value;
window.location.href = '<?= base_url('manage/workerlog') ?>?date=' + date + '&type=' + type;
}
function refreshLogs() {
location.reload();
}
function toggleAutoRefresh() {
if (isAutoRefresh) {
clearInterval(autoRefreshInterval);
isAutoRefresh = false;
document.getElementById('autoRefreshBtn').innerHTML = '<i class="fa fa-play"></i> 자동 새로고침 시작';
document.getElementById('autoRefreshBtn').classList.remove('btn-danger');
document.getElementById('autoRefreshBtn').classList.add('btn-success');
} else {
autoRefreshInterval = setInterval(refreshLogs, 10000); // 10초마다
isAutoRefresh = true;
document.getElementById('autoRefreshBtn').innerHTML = '<i class="fa fa-stop"></i> 자동 새로고침 중지';
document.getElementById('autoRefreshBtn').classList.remove('btn-success');
document.getElementById('autoRefreshBtn').classList.add('btn-danger');
}
}
function filterByLevel() {
const level = document.getElementById('levelFilter').value;
const rows = document.querySelectorAll('.log-row');
let visibleCount = 0;
let infoCount = 0;
let errorCount = 0;
let debugCount = 0;
rows.forEach(row => {
const rowLevel = row.getAttribute('data-level');
if (level === 'all' || rowLevel === level) {
row.style.display = '';
visibleCount++;
} else {
row.style.display = 'none';
}
// 통계 계산
if (rowLevel === 'INFO') infoCount++;
if (rowLevel === 'ERROR') errorCount++;
if (rowLevel === 'DEBUG') debugCount++;
});
// 통계 업데이트
if (level === 'all') {
document.getElementById('totalCount').textContent = visibleCount;
}
}
function downloadLog() {
const date = document.getElementById('dateSelect').value;
const type = document.getElementById('typeSelect').value;
window.location.href = '<?= base_url('manage/workerlog/download') ?>?date=' + date + '&type=' + type;
}
// 페이지 로드 시 스크롤을 테이블 하단으로 (최신 로그가 위에 있으므로 생략)
</script>
<style>
.table-responsive {
border: 1px solid #dee2e6;
}
.log-row:hover {
background-color: #f8f9fa !important;
}
.badge {
font-size: 0.75rem;
}
.widget-content {
padding: 1rem;
}
</style>
<?= $this->endSection() ?>

View File

@@ -4,6 +4,17 @@
<style> <style>
th { th {
font-size: 11px; font-size: 11px;
text-align: center;
}
/* 테이블 헤더 좌우 여백 조정 */
#userList thead th {
padding-left: 4px !important;
padding-right: 4px !important;
}
td {
text-align: center;
} }
#userList tbody tr { #userList tbody tr {
@@ -18,24 +29,61 @@
background-color: #ff0000 !important; background-color: #ff0000 !important;
color: #fff !important; color: #fff !important;
} }
/* PC에서 가로 스크롤이 잘리는 대표 구간들 강제 해제 */
.main-card,
.card,
.card-body {
overflow-x: visible !important;
}
/* 만약 레이아웃 wrapper가 숨기고 있으면 이것도 */
.app-main__outer,
.app-main__inner,
.app-main {
overflow-x: visible !important;
}
/* flex 환경에서 필수 */
.app-main,
.app-main__outer,
.app-main__inner,
.card,
.card-body {
min-width: 0;
}
/* 검색 영역 행마다 구분선 추가 */
#frm_srch_info .row.g-3 {
padding: 12px 0;
border-bottom: 1px solid #e9ecef;
}
#frm_srch_info .row.g-3:last-child {
border-bottom: none;
}
</style> </style>
<h1>현장확인인원별배정현황</h1> <div class="col-md-12 col-xl-12">
<div class="row">
<div class="col-md-12 col-xl-12">
<div class="main-card mb-3 card"> <div class="main-card mb-3 card">
<div class="card-header bg-white border-bottom shadow-sm">
<div class="d-flex flex-wrap align-items-center gap-3 card-header-tab">
<div>
<h4 class="mb-0 fw-bold text-dark">현장확인인원별배정현황</h4>
</div>
</div>
</div>
<div class="card-body"> <div class="card-body">
<form class="row align-items-end" id="frm_srch_info" onsubmit="return false;"> <form id="frm_srch_info" onsubmit="return false;">
<!-- 1줄 --> <!-- 1줄 -->
<div class="row mb-3"> <div class="row g-3 mb-3">
<!-- 관할조직 --> <!-- 관할조직 -->
<div class="col-md-2"> <div class="col-md-2">
<label class="form-label mb-1">관할조직</label> <label class="form-label mb-1 text-sm fw-semibold text-muted">관할조직</label>
<div class="row g-2"> <div class="row g-2">
<div class="col-6"> <div class="col-6">
<select class="form-control" name="bonbu" id="bonbu"> <select class="form-control form-control-sm" name="bonbu" id="bonbu">
<option value="">선택</option> <option value="">선택</option>
<?php foreach ($bonbu as $d): ?> <?php foreach ($bonbu as $d): ?>
<option value="<?= $d['dept_sq'] ?>"><?= $d['dept_nm'] ?></option> <option value="<?= $d['dept_sq'] ?>"><?= $d['dept_nm'] ?></option>
@@ -43,7 +91,7 @@
</select> </select>
</div> </div>
<div class="col-6"> <div class="col-6">
<select class="form-control" name="dept_sq" id="dept_sq"> <select class="form-control form-control-sm" name="dept_sq" id="dept_sq">
<option value="">선택</option> <option value="">선택</option>
</select> </select>
</div> </div>
@@ -52,8 +100,8 @@
<!-- 유형 --> <!-- 유형 -->
<div class="col-md-1"> <div class="col-md-1">
<label class="form-label mb-1">유형</label> <label class="form-label mb-1 text-sm fw-semibold text-muted">유형</label>
<select class="form-control" name="schDateGb"> <select class="form-control form-control-sm" name="schDateGb">
<option value="1">예약일자</option> <option value="1">예약일자</option>
<option value="2">등록일자</option> <option value="2">등록일자</option>
</select> </select>
@@ -61,24 +109,24 @@
<!-- 기준일자 --> <!-- 기준일자 -->
<div class="col-md-3"> <div class="col-md-3">
<label class="form-label mb-1">기준일자</label> <label class="form-label mb-1 text-sm fw-semibold text-muted">기준일자</label>
<div class="row g-2"> <div class="row g-2">
<div class="col-5"> <div class="col-5">
<input type="date" class="form-control" id="sdate" name="sdate"> <input type="date" class="form-control form-control-sm" id="sdate" name="sdate">
</div> </div>
<div class="col-2 d-flex align-items-center justify-content-center">~</div> <div class="col-2 d-flex align-items-center justify-content-center">~</div>
<div class="col-5"> <div class="col-5">
<input type="date" class="form-control" id="edate" name="edate"> <input type="date" class="form-control form-control-sm" id="edate" name="edate">
</div> </div>
</div> </div>
</div> </div>
<!-- 지역검색 --> <!-- 지역검색 -->
<div class="col-md-4"> <div class="col-md-4">
<label class="form-label mb-1">지역검색</label> <label class="form-label mb-1 text-sm fw-semibold text-muted">지역검색</label>
<div class="row g-2"> <div class="row g-2">
<div class="col-4"> <div class="col-4">
<select class="form-control" name="srcSido" id="srcSido"> <select class="form-control form-control-sm" name="srcSido" id="srcSido">
<option value="">시/도</option> <option value="">시/도</option>
<?php foreach ($sido as $s): ?> <?php foreach ($sido as $s): ?>
<option value="<?= $s['region_cd'] ?>"><?= $s['region_nm'] ?></option> <option value="<?= $s['region_cd'] ?>"><?= $s['region_nm'] ?></option>
@@ -86,12 +134,12 @@
</select> </select>
</div> </div>
<div class="col-4"> <div class="col-4">
<select class="form-control" name="srcGugun" id="srcGugun"> <select class="form-control form-control-sm" name="srcGugun" id="srcGugun">
<option value="">시/군/구</option> <option value="">시/군/구</option>
</select> </select>
</div> </div>
<div class="col-4"> <div class="col-4">
<select class="form-control" name="srcDong" id="srcDong"> <select class="form-control form-control-sm" name="srcDong" id="srcDong">
<option value="">읍/면/동</option> <option value="">읍/면/동</option>
</select> </select>
</div> </div>
@@ -100,11 +148,11 @@
</div> </div>
<!-- 2줄 --> <!-- 2줄 -->
<div class="row mb-3"> <div class="row g-3">
<!-- 검색유형 --> <!-- 검색유형 -->
<div class="col-md-1"> <div class="col-md-1">
<label class="form-label mb-1">검색유형</label> <label class="form-label mb-1 text-sm fw-semibold text-muted">검색유형</label>
<select class="form-control" name="srchType"> <select class="form-control form-control-sm" name="srchType">
<option value="">선택</option> <option value="">선택</option>
<option value="1">아이디</option> <option value="1">아이디</option>
<option value="2">이름</option> <option value="2">이름</option>
@@ -113,13 +161,13 @@
<!-- 검색어 --> <!-- 검색어 -->
<div class="col-md-2"> <div class="col-md-2">
<label class="form-label mb-1">검색어</label> <label class="form-label mb-1 text-sm fw-semibold text-muted">검색어</label>
<input type="text" class="form-control" name="srchTxt" placeholder="검색어 입력"> <input type="text" class="form-control form-control-sm" name="srchTxt" placeholder="검색어 입력">
</div> </div>
<!-- 검색버튼 --> <!-- 검색버튼 -->
<div class="col-md-1 d-grid align-items-end"> <div class="col-md-1 d-flex align-items-end">
<button type="button" class="btn btn-primary" id="btnSearch">검색</button> <button type="button" class="btn btn-primary btn-sm w-100" id="btnSearch">검색</button>
</div> </div>
</div> </div>
@@ -127,20 +175,21 @@
</div> </div>
</div> </div>
</div> </div>
<div class="col-md-12 col-xl-12">
<div class="col-md-12 col-xl-12">
<div class="main-card mb-3 card"> <div class="main-card mb-3 card">
<div class="card-header d-flex align-items-center"> <div class="card-header bg-white border-bottom shadow-sm">
<h3 class="card-title mb-0">사용자 목록</h3> <div class="d-flex flex-wrap align-items-center w-100 justify-content-between card-header-tab">
<div class="ms-auto d-flex align-items-center gap-3"> <h4 class="mb-0 fw-bold text-dark">사용자 목록</h4>
<button class="mb-2 me-2 border-0 btn-transition btn btn-shadow btn-outline-success" <div class="d-flex align-items-center gap-2 ms-auto">
id="excel-download"> <button class="btn btn-sm btn-outline-success" id="excel-download">
<i class="fa fa-fw" aria-hidden="true" title="file-excel-o"></i>엑셀다운로드 <i class="fa fa-fw" aria-hidden="true" title="file-excel-o"></i>
엑셀다운로드
</button> </button>
</div> </div>
</div> </div>
</div>
<div class="card-body"> <div class="card-body">
<table id="userList" class="table table-hover table-striped table-bordered"> <table id="userList" class="table table-hover table-striped table-bordered">
<thead> <thead>
@@ -174,7 +223,6 @@
</table> </table>
</div> </div>
</div> </div>
</div>
</div> </div>
@@ -269,7 +317,8 @@
let table = $('#userList').DataTable({ let table = $('#userList').DataTable({
language: lang_kor, language: lang_kor,
processing: true, processing: true,
serverSide: false, serverSide: true,
pageLength: 25,
ajax: { ajax: {
url: '/results/assign/getUserList', url: '/results/assign/getUserList',
type: 'GET', type: 'GET',
@@ -291,20 +340,17 @@
d.srchType = $("#frm_srch_info [name=srchType]").val() d.srchType = $("#frm_srch_info [name=srchType]").val()
d.srchTxt = $("#frm_srch_info [name=srchTxt]").val() d.srchTxt = $("#frm_srch_info [name=srchTxt]").val()
d.start = d.start || 0
d.length = d.length || 10
} }
}, },
"columnDefs": [ columnDefs: [
{ 'targets': '_all', "defaultContent": "" }, { targets: '_all', defaultContent: "" },
{ 'className': 'text-center', 'targets': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] }, { className: 'text-center', targets: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] },
], ],
columns: [ columns: [
{ {
"data": null, data: null,
"width": "50px", width: "50px",
"render": function (data, type, row, meta) { render: function (data, type, row, meta) {
return meta.row + meta.settings._iDisplayStart + 1; return meta.row + meta.settings._iDisplayStart + 1;
} }
}, },
@@ -325,11 +371,9 @@
{ data: 'PM07' }, { data: 'PM07' },
{ data: 'TODAY' }, { data: 'TODAY' },
], ],
// 옵션들 예시
paging: true, paging: true,
searching: false, searching: false,
ordering: false, ordering: false,
serverSide: true,
}); });

View File

@@ -0,0 +1,534 @@
<?= $this->extend('layouts/main') ?>
<?= $this->section('content') ?>
<style>
th {
font-size: 11px;
}
#userList tbody tr {
cursor: pointer;
}
.blockUI {
z-index: 1500 !important;
}
.swal2-cancel {
background-color: #ff0000 !important;
color: #fff !important;
}
</style>
<h1>현장확인인원별배정현황</h1>
<div class="row">
<div class="col-md-12 col-xl-12">
<div class="main-card mb-3 card">
<div class="card-body">
<form class="row align-items-end" id="frm_srch_info" onsubmit="return false;">
<!-- 1줄 -->
<div class="row mb-3">
<!-- 관할조직 -->
<div class="col-md-2">
<label class="form-label mb-1">관할조직</label>
<div class="row g-2">
<div class="col-6">
<select class="form-control" name="bonbu" id="bonbu">
<option value="">선택</option>
<?php foreach ($bonbu as $d): ?>
<option value="<?= $d['dept_sq'] ?>"><?= $d['dept_nm'] ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-6">
<select class="form-control" name="dept_sq" id="dept_sq">
<option value="">선택</option>
</select>
</div>
</div>
</div>
<!-- 유형 -->
<div class="col-md-1">
<label class="form-label mb-1">유형</label>
<select class="form-control" name="schDateGb">
<option value="1">예약일자</option>
<option value="2">등록일자</option>
</select>
</div>
<!-- 기준일자 -->
<div class="col-md-3">
<label class="form-label mb-1">기준일자</label>
<div class="row g-2">
<div class="col-5">
<input type="date" class="form-control" id="sdate" name="sdate">
</div>
<div class="col-2 d-flex align-items-center justify-content-center">~</div>
<div class="col-5">
<input type="date" class="form-control" id="edate" name="edate">
</div>
</div>
</div>
<!-- 지역검색 -->
<div class="col-md-4">
<label class="form-label mb-1">지역검색</label>
<div class="row g-2">
<div class="col-4">
<select class="form-control" name="srcSido" id="srcSido">
<option value="">시/도</option>
<?php foreach ($sido as $s): ?>
<option value="<?= $s['region_cd'] ?>"><?= $s['region_nm'] ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-4">
<select class="form-control" name="srcGugun" id="srcGugun">
<option value="">시/군/구</option>
</select>
</div>
<div class="col-4">
<select class="form-control" name="srcDong" id="srcDong">
<option value="">읍/면/동</option>
</select>
</div>
</div>
</div>
</div>
<!-- 2줄 -->
<div class="row mb-3">
<!-- 검색유형 -->
<div class="col-md-1">
<label class="form-label mb-1">검색유형</label>
<select class="form-control" name="srchType">
<option value="">선택</option>
<option value="1">아이디</option>
<option value="2">이름</option>
</select>
</div>
<!-- 검색어 -->
<div class="col-md-2">
<label class="form-label mb-1">검색어</label>
<input type="text" class="form-control" name="srchTxt" placeholder="검색어 입력">
</div>
<!-- 검색버튼 -->
<div class="col-md-1 d-grid align-items-end">
<button type="button" class="btn btn-primary" id="btnSearch">검색</button>
</div>
</div>
</form>
</div>
</div>
</div>
<div class="col-md-12 col-xl-12">
<div class="main-card mb-3 card">
<div class="card-header d-flex align-items-center">
<h3 class="card-title mb-0">사용자 목록</h3>
<div class="ms-auto d-flex align-items-center gap-3">
<button class="mb-2 me-2 border-0 btn-transition btn btn-shadow btn-outline-success"
id="excel-download">
<i class="fa fa-fw" aria-hidden="true" title="file-excel-o"></i>엑셀다운로드
</button>
</div>
</div>
<div class="card-body">
<table id="userList" class="table table-hover table-striped table-bordered">
<thead>
<tr>
<th rowspan="2">순번</th>
<th rowspan="2">아이디</th>
<th rowspan="2">이름</th>
<th colspan="5" style="text-align: center;">오전</th>
<th colspan="8" style="text-align: center;">오후</th>
<th rowspan="2">합계</th>
</tr>
<tr>
<th width="50">무관</th>
<th width="50">9</th>
<th width="50">10</th>
<th width="50">11</th>
<th width="50">12</th>
<th width="50">무관</th>
<th width="50">1</th>
<th width="50">2</th>
<th width="50">3</th>
<th width="50">4</th>
<th width="50">5</th>
<th width="50">6</th>
<th width="50">7</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div>
</div>
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.13.6/css/jquery.dataTables.min.css" />
<script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script>
<script defer src="/architectui/assets/js/datatable.kor.js"></script>
<script type="text/javascript">
// const tpl = document.querySelector('.my-loader-template');
var teamArr = <?= json_encode($team, JSON_UNESCAPED_UNICODE); ?>;
let date = new Date();
$(function () {
$("#bonbu").on("change", function (e) {
var dept_sq = e.target.value
$("#dept_sq").empty()
var str = "<option value=''>선택</option>"
if (teamArr != null) {
for (var i = 0; i < teamArr.length; i++) {
if (dept_sq === teamArr[i].pdept_sq) {
str += "<option value='" + teamArr[i].dept_sq + "'>" + teamArr[i].dept_nm + "</option>"
}
}
}
$("#dept_sq").append(str)
});
$("#srcSido, #srcGugun").on("change", function (e) {
const targetId = this.id;
$.ajax({
url: "/manage/areas/getAreaList",
method: "POST",
dataType: "json",
data: {
'srcSido': $("#frm_srch_info [name=srcSido]").val(),
'srcGugun': $("#frm_srch_info [name=srcGugun]").val(),
},
beforeSend: function () {
blockUI.blockPage({
message: tpl
})
},
complete: function () {
blockUI.unblockPage()
},
success: function (result) {
switch (targetId) {
case "srcSido":
$("#srcGugun").empty()
var str = "";
str += "<option value=''>시/군/구</option>";
if (result.length > 0) {
for (var i = 0; i < result.length; i++) {
str += "<option value='" + result[i]['region_cd'] + "'>" + result[i].region_nm + "</option>";
}
}
$("#srcGugun").append(str);
break;
case "srcGugun":
$("#srcDong").empty()
var str = "";
str += "<option value=''>읍/면/동</option>";
if (result.length > 0) {
for (var i = 0; i < result.length; i++) {
str += "<option value='" + result[i]['region_cd'] + "'>" + result[i].region_nm + "</option>";
}
}
$("#srcDong").append(str);
break;
}
}
});
});
let table = $('#userList').DataTable({
language: lang_kor,
processing: true,
serverSide: false,
ajax: {
url: '/results/assign/getUserList',
type: 'GET',
beforeSend: function () {
blockUI.blockPage({
message: tpl
})
},
complete: function () {
blockUI.unblockPage()
},
data: function (d) {
d.schDateGb = $("#frm_srch_info [name=schDateGb]").val()
d.sdate = $("#frm_srch_info [name=sdate]").val()
d.edate = $("#frm_srch_info [name=edate]").val()
d.bonbu = $("#frm_srch_info [name=bonbu]").val()
d.team = $("#frm_srch_info [name=dept_sq]").val()
d.srchType = $("#frm_srch_info [name=srchType]").val()
d.srchTxt = $("#frm_srch_info [name=srchTxt]").val()
d.start = d.start || 0
d.length = d.length || 10
}
},
"columnDefs": [
{ 'targets': '_all', "defaultContent": "" },
{ 'className': 'text-center', 'targets': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] },
],
columns: [
{
"data": null,
"width": "50px",
"render": function (data, type, row, meta) {
return meta.row + meta.settings._iDisplayStart + 1;
}
},
{ data: 'usr_id' },
{ data: 'usr_nm' },
{ data: 'AMETC' },
{ data: 'AM09' },
{ data: 'AM10' },
{ data: 'AM11' },
{ data: 'AM12' },
{ data: 'PMETC' },
{ data: 'PM01' },
{ data: 'PM02' },
{ data: 'PM03' },
{ data: 'PM04' },
{ data: 'PM05' },
{ data: 'PM06' },
{ data: 'PM07' },
{ data: 'TODAY' },
],
// 옵션들 예시
paging: true,
searching: false,
ordering: false,
serverSide: true,
});
$('#userList tbody').on('click', 'tr', function () {
const row = table.row(this).data()
if (!row) return
location.href = '/article/receipt/lists?usr_id=' + row.usr_id;
});
// [검색] 버튼 눌렀을 때 다시 조회
$('#btnSearch').on('click', function () {
table.ajax.reload()
});
// 유저 등록 모달
$("#addUser").on("click", function () {
// $("#frm_user_info")[0].reset()
// $("#frm_user_info [name=usr_sq]").val("")
// $("#frm_user_info [name=type]").val("create")
// $("#frm_user_info [name=addUserId]").prop("readonly", false)
// const modalEl = document.getElementById('userModal');
// const myModal = new bootstrap.Modal(modalEl);
// myModal.show();
});
// 엑셀다운 click
$("#excel-download").on("click", function () {
$.ajax({
url: "/results/assign/excel",
method: "GET",
dataType: "json",
data: $("#frm_srch_info").serialize(),
beforeSend: function () {
blockUI.blockPage({
message: tpl
})
},
complete: function () {
blockUI.unblockPage()
},
success: function (result) {
// downloadExcel(result.data);
downloadExcelWithHeader(result.data);
}
});
});
});
// 엑셀 다운로드
function downloadExcelWithHeader(dataRows) {
// 1) 헤더 (16컬럼)
const header1 = [
"아이디", "이름",
"오전", "", "", "", "",
"오후", "", "", "", "", "", "", "",
"합계"
];
const header2 = [
"", "",
"무관", "9", "10", "11", "12",
"무관", "1", "2", "3", "4", "5", "6", "7",
""
];
// 2) 바디 (16컬럼 정확히 맞춤)
const body = dataRows.map(r => ([
r.usr_id,
r.usr_nm,
r.AMETC, r.AM09, r.AM10, r.AM11, r.AM12,
r.PMETC, r.PM01, r.PM02, r.PM03, r.PM04, r.PM05, r.PM06, r.PM07,
r.TODAY
]));
const aoa = [header1, header2, ...body];
// 3) 시트 생성
const ws = XLSX.utils.aoa_to_sheet(aoa);
// 4) 병합(colspan / rowspan)
ws["!merges"] = [
// 아이디, 이름 (rowspan 2)
{ s: { r: 0, c: 0 }, e: { r: 1, c: 0 } }, // A1:A2
{ s: { r: 0, c: 1 }, e: { r: 1, c: 1 } }, // B1:B2
// 오전 (colspan 5)
{ s: { r: 0, c: 2 }, e: { r: 0, c: 6 } }, // C1:G1
// 오후 (colspan 8)
{ s: { r: 0, c: 7 }, e: { r: 0, c: 14 } }, // H1:O1
// 합계 (rowspan 2)
{ s: { r: 0, c: 15 }, e: { r: 1, c: 15 } }, // P1:P2
];
ws['!cols'] = [
{ wpx: 80 }, // A: 아이디
{ wpx: 100 }, // B: 이름
{ wpx: 50 }, // C: 오전-무관
{ wpx: 50 }, // D: 오전-9
{ wpx: 50 }, // E: 오전-10
{ wpx: 50 }, // F: 오전-11
{ wpx: 50 }, // G: 오전-12
{ wpx: 50 }, // H: 오후-무관
{ wpx: 50 }, // I: 오후-1
{ wpx: 50 }, // J: 오후-2
{ wpx: 50 }, // K: 오후-3
{ wpx: 50 }, // L: 오후-4
{ wpx: 50 }, // M: 오후-5
{ wpx: 50 }, // N: 오후-6
{ wpx: 50 }, // O: 오후-7
{ wpx: 60 }, // P: 합계
];
// 6) 저장
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "Sheet1");
XLSX.writeFile(
wb,
"현장확인인원별배정현황" + getDateTimeString() + ".xlsx"
);
}
// 엑셀 다운로드
function downloadExcel(data) {
const ws = XLSX.utils.json_to_sheet(data);
ws['!cols'] = [
{ wpx: 80 },
{ wpx: 80 },
{ wpx: 80 },
{ wpx: 80 },
{ wpx: 80 },
{ wpx: 80 },
{ wpx: 80 },
{ wpx: 80 },
{ wpx: 80 },
{ wpx: 80 },
{ wpx: 80 },
{ wpx: 80 },
{ wpx: 80 },
{ wpx: 80 },
{ wpx: 80 },
{ wpx: 80 },
];
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "Sheet1");
const wbout = XLSX.write(wb, { bookType: 'xlsx', type: 'array' });
const blob = new Blob([wbout], { type: 'application/octet-stream' });
const link = document.createElement("a");
link.href = URL.createObjectURL(blob);
link.download = "현장확인인원별배정현황" + getDateTimeString() + ".xlsx";
link.click();
URL.revokeObjectURL(link.href);
}
function getDateTimeString() {
const d = new Date();
const yyyy = d.getFullYear();
const mm = String(d.getMonth() + 1).padStart(2, '0');
const dd = String(d.getDate()).padStart(2, '0');
const hh = String(d.getHours()).padStart(2, '0');
const mi = String(d.getMinutes()).padStart(2, '0');
const ss = String(d.getSeconds()).padStart(2, '0');
return `${yyyy}${mm}${dd}${hh}${mi}${ss}`;
}
window.addEventListener("DOMContentLoaded", function () {
// 오늘 날짜
const today = new Date();
// 이번 달 1일
const firstDay = new Date(today.getFullYear(), today.getMonth(), 1);
// 이번 달 말일 (다음달 0일 = 이번달 말일)
const lastDay = new Date(today.getFullYear(), today.getMonth() + 1, 0);
// yyyy-mm-dd 형태로 변환
const toDate = (d) => d.toISOString().split("T")[0];
document.getElementById("sdate").value = toDate(firstDay);
document.getElementById("edate").value = toDate(lastDay);
});
</script>
<?= $this->endSection() ?>

View File

@@ -32,6 +32,7 @@ if (!empty($data['cert_register']) && $data['cert_register_save_yn'] != 'Y') { /
<?= $this->extend('layouts/main') ?> <?= $this->extend('layouts/main') ?>
<?= $this->section('content') ?> <?= $this->section('content') ?>
<link rel="stylesheet" href="/common/css/custom.css">
<style> <style>
table th { table th {
vertical-align: middle; vertical-align: middle;
@@ -62,18 +63,16 @@ if (!empty($data['cert_register']) && $data['cert_register_save_yn'] != 'Y') { /
max-width: 180px; max-width: 180px;
} }
.card-header {
display: flex !important;
align-items: center;
}
.card-header-tab {
justify-content: flex-start !important;
}
.table-scroll { .table-scroll {
max-height: 300px; max-height: 300px;
overflow-y: scroll; overflow-y: scroll;
min-height: 100px;
}
/* 정보변경 이력 테이블 깜박임 방지 */
.apt-info-table {
table-layout: fixed;
width: 100%;
} }
.swal2-cancel { .swal2-cancel {
@@ -93,14 +92,6 @@ if (!empty($data['cert_register']) && $data['cert_register_save_yn'] != 'Y') { /
} }
</style> </style>
<div class="app-page-title">
<div class="page-title-wrapper">
<div class="page-title-heading">
<div>확인매물 상세 내용</div>
</div>
</div>
</div>
<form name="rcptFrm" id="rcptFrm" method="post" action="" enctype="multipart/form-data" autocomplete="off" <form name="rcptFrm" id="rcptFrm" method="post" action="" enctype="multipart/form-data" autocomplete="off"
onsubmit="return false;"> onsubmit="return false;">
<input type="hidden" name="address_code" id="address_code" value="<?= $data['address_code'] ?>" /> <input type="hidden" name="address_code" id="address_code" value="<?= $data['address_code'] ?>" />
@@ -113,30 +104,19 @@ if (!empty($data['cert_register']) && $data['cert_register_save_yn'] != 'Y') { /
<div class="col-md-12 col-xl-12"> <div class="col-md-12 col-xl-12">
<div class="col-lg-12"> <div class="col-lg-12">
<div class="main-card mb-3 card"> <div class="main-card mb-2 card">
<div class="card-header" style="width:100%; max-width:100%; min-width:600px; padding:0; border:0;"> <div class="card-header bg-white border-bottom shadow-sm">
<p class="left"> <div class="d-flex flex-wrap align-items-center w-100 justify-content-between card-header-tab">
</p> <h4 class="mb-0 fw-bold text-dark">확인매물 상세 내용</h4>
<table style="width:100%; min-width:600px; padding:0; border:0;" cellpadding="0" cellspacing="0" <div class="d-flex align-items-center flex-nowrap gap-4 ms-auto" style="white-space:nowrap;">
border="0" width="100%"> <span class="text-muted me-2">매물ID:</span>
<tbody> <span class="fw-bold text-primary fs-6 me-4"> <?= $data['atcl_no'] ?> </span>
<tr> <span class="text-muted me-2">CP ID:</span>
<td style="width: 50%;padding-left: 20px"><span class="tit">매물ID :</span> <span <span class="fw-bold text-primary fs-6 me-4"> <?= $data['cpid'] ?> </span>
class="num"><?= $data['atcl_no'] ?></span> <span class="text-muted me-2">현재 상태:</span>
</td> <span class="fw-bold text-danger fs-6"> <?= $data['pre_stat'] ?> </span>
<td style="width: 20%;"><span class="tit">CP ID :</span> <span </div>
class="num"><?= $data['cpid'] ?></span></td> </div>
<td style="width: 30%; text-align: right;padding-right: 20px"><span class="tit">현재 상태
:</span> <span class="num"><?= $data['pre_stat'] ?></span></td>
</tr>
<tr>
<td height="15"></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
<p></p>
</div> </div>
<div class="card-body"> <div class="card-body">
<h5 class="card-title">공인 중개사 정보</h5> <h5 class="card-title">공인 중개사 정보</h5>
@@ -158,16 +138,16 @@ if (!empty($data['cert_register']) && $data['cert_register_save_yn'] != 'Y') { /
</div> </div>
</div> </div>
<div class="main-card mb-3 card"> <div class="main-card mb-2 card">
<div class="card-body"> <div class="card-body">
<h5 class="card-title">매물 정보</h5> <h5 class="card-title">매물 정보</h5>
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-bordered table-sm tbl_basic2 align-middle mb-0 apt-info-table"> <table class="table table-bordered table-sm tbl_basic2 align-middle mb-0 apt-info-table">
<colgroup> <colgroup>
<col style="width:120px"> <col width="15%" />
<col style="width:320px"> <col width="35%" />
<col style="width:120px"> <col width="15%" />
<col style="width:320px"> <col width="35%" />
</colgroup> </colgroup>
<tbody> <tbody>
@@ -180,12 +160,10 @@ if (!empty($data['cert_register']) && $data['cert_register_save_yn'] != 'Y') { /
<select class="form-select form-select-sm" name="atcl_vrtc_way" <select class="form-select form-select-sm" name="atcl_vrtc_way"
id="atcl_vrtc_way" disabled> id="atcl_vrtc_way" disabled>
<option value="">-선택-</option> <option value="">-선택-</option>
<?php foreach ($codes as $c): ?> <?php foreach (($codes['VRFCREQ_WAY']['items'] ?? []) as $cd => $cdNm): ?>
<?php if ($c['category'] === "VRFCREQ_WAY"): ?> <option value="<?= $cd ?>" <?= ($cd === $data['vrfc_type_cd']) ? 'selected' : '' ?>>
<option value="<?= $c['cd'] ?>" <?= ($c['cd'] === $data['vrfc_type_cd']) ? 'selected' : '' ?>> <?= $cdNm ?>
<?= $c['cd_nm'] ?>
</option> </option>
<?php endif; ?>
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
<?= $data['update_res_tm'] ?> <?= $data['update_res_tm'] ?>
@@ -300,7 +278,7 @@ if (!empty($data['cert_register']) && $data['cert_register_save_yn'] != 'Y') { /
<th>가격</th> <th>가격</th>
<td> <td>
<!-- 기본 가격 --> <!-- 기본 가격 -->
<div class="align-items-center gap-2 mb-2" id="div_trade_type_price" <div class="align-items-center gap-2 mb-0" id="div_trade_type_price"
style="display: none;"> style="display: none;">
<div class="input-group input-group-sm" style="max-width: 220px;"> <div class="input-group input-group-sm" style="max-width: 220px;">
<input type="text" class="form-control text-end" name="atcl_amt1" <input type="text" class="form-control text-end" name="atcl_amt1"
@@ -319,7 +297,7 @@ if (!empty($data['cert_register']) && $data['cert_register_save_yn'] != 'Y') { /
<?php endif; ?> <?php endif; ?>
</div> </div>
<div class="d-flex align-items-center gap-1 mb-2" <div class="d-flex align-items-center gap-1 mb-0"
id="div_trade_type_price_monthly" style="display: none;"> id="div_trade_type_price_monthly" style="display: none;">
<div class="input-group input-group-sm" style="max-width: 220px;"> <div class="input-group input-group-sm" style="max-width: 220px;">
<input type="text" class="form-control text-end" name="atcl_amt2" <input type="text" class="form-control text-end" name="atcl_amt2"
@@ -554,9 +532,9 @@ if (!empty($data['cert_register']) && $data['cert_register_save_yn'] != 'Y') { /
<?php <?php
$apt_rlet_type_cd = ['A01', 'A02', 'A03', 'A04', 'B01', 'B02', 'B03']; $apt_rlet_type_cd = ['A01', 'A02', 'A03', 'A04', 'B01', 'B02', 'B03'];
$villa_rlet_type_cd = ['A05', 'A06']; $villa_rlet_type_cd = ['A05', 'A06'];
if (in_array($detail_hscp['aptType'], $apt_rlet_type_cd)): if (isset($detail_hscp['aptType']) && in_array($detail_hscp['aptType'], $apt_rlet_type_cd)):
?> ?>
<div class="main-card mb-3 card"> <div class="main-card mb-2 card">
<div class="card-body "> <div class="card-body ">
<h5 class="card-title">단지 정보</h5> <h5 class="card-title">단지 정보</h5>
<table class="table table-bordered table-sm tbl_basic2 apt-info-table"> <table class="table table-bordered table-sm tbl_basic2 apt-info-table">
@@ -578,9 +556,9 @@ if (!empty($data['cert_register']) && $data['cert_register_save_yn'] != 'Y') { /
<?php endif; ?> <?php endif; ?>
<?php <?php
if (in_array($detail_hscp['aptType'], $villa_rlet_type_cd)): if (isset($detail_hscp['aptType']) && in_array($detail_hscp['aptType'], $villa_rlet_type_cd)):
?> ?>
<div class="main-card mb-3 card"> <div class="main-card mb-2 card">
<div class="card-body "> <div class="card-body ">
<h5 class="card-title">단지 정보</h5> <h5 class="card-title">단지 정보</h5>
<table class="table table-bordered table-sm tbl_basic2 apt-info-table"> <table class="table table-bordered table-sm tbl_basic2 apt-info-table">
@@ -614,20 +592,18 @@ if (!empty($data['cert_register']) && $data['cert_register_save_yn'] != 'Y') { /
<?php endif; ?> <?php endif; ?>
<?php endif; ?> <?php endif; ?>
<div class="main-card mb-3 card"> <div class="main-card mb-2 card">
<div class="card-body "> <div class="card-body ">
<h5 class="card-title">상태변경</h5> <h5 class="card-title">상태변경</h5>
<?php foreach ($codes as $c): ?> <?php foreach (($codes['STEP_VERIFICATION']['items'] ?? []) as $cd => $cdNm): ?>
<?php if ($c['category'] === "STEP_VERIFICATION"): ?>
<button class="mb-2 me-2 btn btn-light" <button class="mb-2 me-2 btn btn-light"
onclick="fn_status_change(<?= $data['vr_sq'] ?>, '<?= $c['cd'] ?>', '<?= $c['cd_nm'] ?>');"><?= $c['cd_nm'] ?></button> onclick="fn_status_change(<?= $data['vr_sq'] ?>, '<?= $cd ?>', '<?= $cdNm ?>');"><?= $cdNm ?></button>
<?php endif; ?>
<?php endforeach; ?> <?php endforeach; ?>
</div> </div>
</div> </div>
<div class="main-card mb-3 card" id="docu_chk" style="display: none;"> <div class="main-card mb-2 card" id="docu_chk" style="display: none;">
<div class="card-body"> <div class="card-body">
<h5 class="card-title">서류확인 정보</h5> <h5 class="card-title">서류확인 정보</h5>
@@ -645,14 +621,19 @@ if (!empty($data['cert_register']) && $data['cert_register_save_yn'] != 'Y') { /
<?php if (!empty($data['confirm_doc_img_url'])): ?> <?php if (!empty($data['confirm_doc_img_url'])): ?>
<?php if ($data['confirm_doc_img_url_save_yn'] == 'Y'): <?php if ($data['confirm_doc_img_url_save_yn'] == 'Y'):
if (!empty($record)) {
$img_path = $record['file_path'] . $record['file_name']; $img_path = $record['file_path'] . $record['file_name'];
$ext = $record['file_ext']; $ext = $record['file_ext'];
} else {
$img_path = '';
$ext = '';
}
if (strtolower($ext) == '.pdf'): if (strtolower($ext) == '.pdf'):
?> ?>
<iframe src="<?= $server_addr ?><?= $img_path ?>" frameborder="0" <iframe src="<?= $server_addr ?><?= $img_path ?>" frameborder="0" loading="lazy"
style="padding: 10px 0;width:100%; height:800px;"></iframe><br> style="padding: 10px 0;width:100%; height:800px;"></iframe><br>
<?php elseif (!in_array($ext, ['.zip', '.ZIP', '.pdf', '.PDF'])): ?> <?php elseif (!in_array($ext, ['.zip', '.ZIP', '.pdf', '.PDF'])): ?>
<?php if (!empty($arrRecord) && is_array($arrRecord)): ?>
<?php foreach ($arrRecord as $row): <?php foreach ($arrRecord as $row):
$arr_img_path = $row['file_path'] . $row['file_name']; $arr_img_path = $row['file_path'] . $row['file_name'];
?> ?>
@@ -664,6 +645,7 @@ if (!empty($data['cert_register']) && $data['cert_register_save_yn'] != 'Y') { /
</a> </a>
<?php endforeach; ?> <?php endforeach; ?>
<?php endif; ?> <?php endif; ?>
<?php endif; ?>
<?php else: <?php else:
$img_path = $data['confirm_doc_img_url']; $img_path = $data['confirm_doc_img_url'];
@@ -671,18 +653,18 @@ if (!empty($data['cert_register']) && $data['cert_register_save_yn'] != 'Y') { /
if (strtolower($ext) == '.pdf'): if (strtolower($ext) == '.pdf'):
?> ?>
<iframe src="<?= $img_path ?>" frameborder="0" <iframe src="<?= $img_path ?>" frameborder="0" loading="lazy"
style="padding: 10px 0;width:100%; height:800px;"></iframe> style="padding: 10px 0;width:100%; height:800px;"></iframe>
<?php else: ?> <?php else: ?>
<?php endif; ?> <?php endif; ?>
<img id="photo-display" src="<?= $img_path ?>" alt="Image" <img id="photo-display" src="<?= $img_path ?>" alt="Image" loading="lazy"
style="width:100%;max-width:945px;min-width:100%;border:0;" /> style="width:100%;max-width:945px;min-width:100%;border:0;" />
<?php endif; ?> <?php endif; ?>
<?php else: ?> <?php else: ?>
<a href="#" rel="lightbox[gallery]"> <a href="#" rel="lightbox[gallery]">
<img id="photo-display" src="/plugin/img/photo.gif" alt="Image" <img id="photo-display" src="/plugin/img/photo.gif" alt="Image" loading="lazy"
style="width:100%; max-width:945px; border:0;"> style="width:100%; max-width:945px; border:0;">
</a> </a>
@@ -702,7 +684,7 @@ if (!empty($data['cert_register']) && $data['cert_register_save_yn'] != 'Y') { /
<!-- 우측 : 정보 --> <!-- 우측 : 정보 -->
<td valign="top" class="pt-2"> <td valign="top" class="pt-2">
<table class="table table-sm w-100 tbl_basic2 table-bordered"> <table class="table table-sm tbl_basic2 table-bordered">
<colgroup> <colgroup>
<col width="30%"> <col width="30%">
<col> <col>
@@ -720,12 +702,10 @@ if (!empty($data['cert_register']) && $data['cert_register_save_yn'] != 'Y') { /
<select class="form-select" id="fax_conf_res_d11" <select class="form-select" id="fax_conf_res_d11"
name="fax_conf_res_d11"> name="fax_conf_res_d11">
<option value="">-선택-</option> <option value="">-선택-</option>
<?php foreach ($codes as $c): ?> <?php foreach (($codes['CONFIRM_RESULT_D11']['items'] ?? []) as $cd => $cdNm): ?>
<?php if ($c['category'] === "CONFIRM_RESULT_D11"): ?> <option value="<?= $cd ?>" <?= ($cd === $data['result_d11']) ? 'selected' : '' ?>>
<option value="<?= $c['cd'] ?>" <?= ($c['cd'] === $data['result_d11']) ? 'selected' : '' ?>> <?= $cdNm ?>
<?= $c['cd_nm'] ?>
</option> </option>
<?php endif; ?>
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
</td> </td>
@@ -734,7 +714,7 @@ if (!empty($data['cert_register']) && $data['cert_register_save_yn'] != 'Y') { /
<tr> <tr>
<th>홍보확인서<br>미확인여부상세</th> <th>홍보확인서<br>미확인여부상세</th>
<td> <td>
<table class="table w-100 tbl_basic2 table-bordered"> <table class="table tbl_basic2 table-bordered">
<tr> <tr>
<?php <?php
$checks = explode('|', $data['comment']); $checks = explode('|', $data['comment']);
@@ -749,12 +729,7 @@ if (!empty($data['cert_register']) && $data['cert_register_save_yn'] != 'Y') { /
]; ];
$nCnt = 0; $nCnt = 0;
$code_comment = []; $code_comment = $codes['CONSULTANT_COMMENT']['items'] ?? [];
foreach ($codes as $c) {
if ($c['category'] === "CONSULTANT_COMMENT") {
array_push($code_comment, $c);
}
}
foreach ($code_comment as $key => $value) { foreach ($code_comment as $key => $value) {
if ($nCnt % 2 == 0 && $nCnt != 0) { if ($nCnt % 2 == 0 && $nCnt != 0) {
@@ -774,7 +749,7 @@ if (!empty($data['cert_register']) && $data['cert_register_save_yn'] != 'Y') { /
id="comment_<?= $key ?>" disabled> id="comment_<?= $key ?>" disabled>
<label class="form-check-label ms-1 small" <label class="form-check-label ms-1 small"
for="price_ignore1"> for="price_ignore1">
<?= $value['cd_nm'] ?> <?= $value ?>
</label> </label>
</div> </div>
</td> </td>
@@ -864,14 +839,14 @@ if (!empty($data['cert_register']) && $data['cert_register_save_yn'] != 'Y') { /
</tbody> </tbody>
</table> </table>
</div> </div>
<div class="card-footer d-flex justify-content-end gap-1"> <div class="card-footer d-flex justify-content-end">
<button type="button" class="btn btn-success btn-sm" onclick="saveDocu();"> <button type="button" class="btn btn-success btn-sm" onclick="saveDocu();">
저장 저장
</button> </button>
</div> </div>
</div> </div>
<div class="main-card mb-3 card" id="call_chk" style="display: none;"> <div class="main-card mb-2 card" id="call_chk" style="display: none;">
<div class="card-body"> <div class="card-body">
<h5 class="card-title">전화확인 정보</h5> <h5 class="card-title">전화확인 정보</h5>
@@ -897,12 +872,10 @@ if (!empty($data['cert_register']) && $data['cert_register_save_yn'] != 'Y') { /
<td> <td>
<select class="form-select" name="tel_agree" id="tel_agree"> <select class="form-select" name="tel_agree" id="tel_agree">
<option value="">-선택-</option> <option value="">-선택-</option>
<?php foreach ($codes as $c): ?> <?php foreach (($codes['CONFIRM_RESULT_T11']['items'] ?? []) as $cd => $cdNm): ?>
<?php if ($c['category'] === "CONFIRM_RESULT_T11"): ?> <option value="<?= $cd ?>" <?php if ($cd === $data['tel_agree']) {
<option value="<?= $c['cd'] ?>" <?php if ($c['cd'] === $data['tel_agree']) {
echo "selected"; echo "selected";
} ?>><?= $c['cd_nm'] ?></option> } ?>><?= $cdNm ?></option>
<?php endif; ?>
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
</td> </td>
@@ -1045,7 +1018,7 @@ if (!empty($data['cert_register']) && $data['cert_register_save_yn'] != 'Y') { /
<tr> <tr>
<th>메모</th> <th>메모</th>
<td> <td>
<div class="d-flex flex-column gap-1" style=""> <div class="d-flex flex-column gap-1">
<textarea class="form-control" name="memo_tel" id="memo_tel" rows="2" <textarea class="form-control" name="memo_tel" id="memo_tel" rows="2"
style="resize: none;"><?= $memo['memo'] ?></textarea> style="resize: none;"><?= $memo['memo'] ?></textarea>
@@ -1059,14 +1032,11 @@ if (!empty($data['cert_register']) && $data['cert_register_save_yn'] != 'Y') { /
<th>통화실패사유</th> <th>통화실패사유</th>
<td class="d-flex gap-2"> <td class="d-flex gap-2">
<select class="form-select" name="tel_fail_cause" id="tel_fail_cause"> <select class="form-select" name="tel_fail_cause" id="tel_fail_cause">
<?php foreach ($codes as $c): ?> <?php foreach (($codes['TEL_FAIL_CAUSE']['items'] ?? []) as $cd => $cdNm): ?>
<?php if ($c['category'] === "TEL_FAIL_CAUSE"): ?> <option value="<?= $cd ?>" <?php if ($cd === $data['tel_fail_cause']) {
<option value="<?= $c['cd'] ?>" <?php if ($c['cd'] === $data['tel_fail_cause']) {
echo 'selected'; echo 'selected';
} ?>><?= $c['cd_nm'] ?></option> } ?>><?= $cdNm ?></option>
<?php endif; ?>
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
</td> </td>
</tr> </tr>
@@ -1086,7 +1056,7 @@ if (!empty($data['cert_register']) && $data['cert_register_save_yn'] != 'Y') { /
</div> </div>
<div class="main-card mb-3 card" id="regibox"> <div class="main-card mb-2 card" id="regibox">
<div class="card-body"> <div class="card-body">
<h5 class="card-title">등기부등본확인 정보</h5> <h5 class="card-title">등기부등본확인 정보</h5>
@@ -1103,12 +1073,12 @@ if (!empty($data['cert_register']) && $data['cert_register_save_yn'] != 'Y') { /
<?php if (!empty($data['cert_register'])): ?> <?php if (!empty($data['cert_register'])): ?>
<?php if ($data['cert_register_save_yn'] == 'Y'): ?> <?php if ($data['cert_register_save_yn'] == 'Y'): ?>
<?php if (strtolower($file_pdf) == 'pdf') { ?> <?php if (strtolower($file_pdf) == 'pdf') { ?>
<iframe src="<?= $server_addr ?><?= $regi_pdf_path ?>" frameborder="0" <iframe src="<?= $server_addr ?><?= $regi_pdf_path ?>" frameborder="0" loading="lazy"
style="padding: 10px 0;width:100%; height:800px;"></iframe><br /> style="padding: 10px 0;width:100%; height:800px;"></iframe><br />
<?php } else { ?> <?php } else { ?>
<div id="regi_file_dis" style="padding: 10px 0px; height: 730px; overflow-y: auto;"> <div id="regi_file_dis" style="padding: 10px 0px; height: 730px; overflow-y: auto;">
<?php if (empty($arrRegist)) { ?> <?php if (empty($arrRegist)) { ?>
<img id="photo-display2" src="/img/photo.gif" alt="Image" <img id="photo-display2" src="/img/photo.gif" alt="Image" loading="lazy"
style="width:100%;max-width:945px;min-width:100%;border:0;" /> style="width:100%;max-width:945px;min-width:100%;border:0;" />
<?php } else { <?php } else {
foreach ($arrRegist as $row) { foreach ($arrRegist as $row) {
@@ -1124,12 +1094,12 @@ if (!empty($data['cert_register']) && $data['cert_register_save_yn'] != 'Y') { /
<?php } ?> <?php } ?>
<?php else: ?> <?php else: ?>
<?php if (strtolower($file_pdf) == 'pdf') { ?> <?php if (strtolower($file_pdf) == 'pdf') { ?>
<iframe src="<?= $regi_pdf_path ?>" frameborder="0" <iframe src="<?= $regi_pdf_path ?>" frameborder="0" loading="lazy"
style="padding: 10px 0;width:100%; height:800px;"></iframe><br> style="padding: 10px 0;width:100%; height:800px;"></iframe><br>
<?php } else { ?> <?php } else { ?>
<div id="regi_file_dis" style="padding: 10px 0px; height: 730px; overflow-y: auto;"> <div id="regi_file_dis" style="padding: 10px 0px; height: 730px; overflow-y: auto;">
<?php if (empty($arr_cert_register)) { ?> <?php if (empty($arr_cert_register)) { ?>
<img id="photo-display2" src="/plugin/img/photo.gif" alt="Image" <img id="photo-display2" src="/plugin/img/photo.gif" alt="Image" loading="lazy"
style="width:100%;max-width:945px;min-width:100%;border:0;" /> style="width:100%;max-width:945px;min-width:100%;border:0;" />
<?php } else { <?php } else {
foreach ($arr_cert_register as $img_path) { ?> foreach ($arr_cert_register as $img_path) { ?>
@@ -1162,7 +1132,7 @@ if (!empty($data['cert_register']) && $data['cert_register_save_yn'] != 'Y') { /
?> ?>
<div id="regi_file_dis" style="padding: 10px 0;"> <div id="regi_file_dis" style="padding: 10px 0;">
<img id="photo-display2" src="<?= $regi_img_path ?>" alt="Image" <img id="photo-display2" src="<?= $regi_img_path ?>" alt="Image" loading="lazy"
style="width:100%;max-width:945px;min-width:100%;border:0;" /> style="width:100%;max-width:945px;min-width:100%;border:0;" />
</div> </div>
<div id="regi_file_pdf" style="padding: 10px 0; display:none;"> <div id="regi_file_pdf" style="padding: 10px 0; display:none;">
@@ -1348,13 +1318,13 @@ if (!empty($data['cert_register']) && $data['cert_register_save_yn'] != 'Y') { /
<div class="main-card mb-3 card"> <div class="main-card mb-2 card">
<div class="card-body "> <div class="card-body ">
<h5 class="card-title">정보변경 이력</h5> <h5 class="card-title">정보변경 이력</h5>
<div class="table-scroll"> <div class="table-scroll">
<table class="table table-bordered table-sm tbl_basic2 apt-info-table"> <table class="table table-bordered table-sm tbl_basic2 apt-info-table">
<tr> <tr>
<th width="90" style="text-align: center;">진행상태</th> <th width="120" style="text-align: center;">진행상태</th>
<th width="150" style="text-align: center;">변경내용</th> <th width="150" style="text-align: center;">변경내용</th>
<th width="90" style="text-align: center;">처리자(ID)</th> <th width="90" style="text-align: center;">처리자(ID)</th>
<th width="120" style="text-align: center;">처리일시</th> <th width="120" style="text-align: center;">처리일시</th>
@@ -1599,6 +1569,26 @@ if (!empty($data['cert_register']) && $data['cert_register_save_yn'] != 'Y') { /
$(function () { $(function () {
// DOM Ready 시 즉시 blockUI 해제
if (typeof blockUI !== 'undefined') {
blockUI.unblockPage();
}
$('.blockUI').remove();
$('body').css('overflow', '');
// 페이지 완전 로드 후에도 다시 한번 해제
$(window).on('load', function() {
setTimeout(function() {
if (typeof blockUI !== 'undefined') {
blockUI.unblockPage();
}
$('.blockUI').remove();
$('.blockOverlay').remove();
$('body').css('overflow', '');
$('body').removeClass('modal-open');
}, 100);
});
if (failChk == "20040") { if (failChk == "20040") {
$("#fail_chk1").attr("checked", true); $("#fail_chk1").attr("checked", true);
} else if (failChk == "20041") { } else if (failChk == "20041") {

View File

@@ -1,76 +1,35 @@
<?= $this->extend('layouts/main') ?> <?= $this->extend('layouts/main') ?>
<?= $this->section('content') ?> <?= $this->section('content') ?>
<style>
th {
font-size: 11px;
text-align: center;
}
td {
text-align: center;
}
#resultList tbody tr {
cursor: pointer;
font-size: 12px;
}
.blockUI {
z-index: 1500 !important;
}
.ellipsis {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 180px;
}
.card-header {
display: flex !important;
align-items: center;
}
.card-header-tab {
justify-content: flex-start !important;
}
.table-scroll {
max-height: 300px;
overflow-y: scroll;
}
.swal2-cancel {
background-color: #ff0000 !important;
color: #fff !important;
}
</style>
<h1>확인매물 현황</h1>
<div class="col-md-12 col-xl-12"> <div class="col-md-12 col-xl-12">
<div class="main-card mb-3 card"> <div class="main-card mb-3 card">
<div class="card-header bg-white border-bottom shadow-sm">
<div class="d-flex flex-wrap align-items-center gap-3 card-header-tab">
<div>
<h4 class="mb-0 fw-bold text-dark">확인매물 현황</h4>
</div>
</div>
</div>
<div class="card-body"> <div class="card-body">
<form id="frm_srch_info" method="get" onsubmit="return false;"> <form id="frm_srch_info" method="get" onsubmit="return false;">
<input type="hidden" name="m" id="m" value="M801" /> <input type="hidden" name="m" id="m" value="M801" />
<input type="hidden" name="todo" id="todo" value="inq" /> <input type="hidden" name="todo" id="todo" value="inq" />
<input type="hidden" name="usr_id" value="" /> <input type="hidden" name="usr_id" value="" />
<!-- 안내 -->
<div class="alert alert-warning py-2 mb-3">
<small class="mb-0">
매물번호를 입력하면 <b>다른 조건은 무시</b>됩니다.
</small>
</div>
<!-- 검색 폼 --> <!-- 검색 폼 -->
<div class="row g-3"> <div class="row g-3">
<!-- 매물번호 --> <!-- 매물번호 -->
<div class="col-md-1"> <div class="col-md-1">
<label class="form-label mb-1">매물번호</label> <label class="form-label mb-1">
매물번호
<i class="pe-7s-info info-tooltip" data-bs-toggle="tooltip" data-bs-placement="top"
title="매물번호를 입력하면 다른 조건은 무시됩니다"></i>
</label>
<input type="text" name="atcl_no" class="form-control form-control-sm" placeholder="매물번호" maxlength="10" <input type="text" name="atcl_no" class="form-control form-control-sm" placeholder="매물번호" maxlength="10"
data-bs-toggle="tooltip" data-bs-placement="top" title="매물번호를 입력하면 다른 조건은 무시됩니다"
onkeypress="atcl_no_enter(event)"> onkeypress="atcl_no_enter(event)">
</div> </div>
@@ -79,10 +38,8 @@
<label class="form-label mb-1">현재상태</label> <label class="form-label mb-1">현재상태</label>
<select name="stat_cd" class="form-select form-select-sm"> <select name="stat_cd" class="form-select form-select-sm">
<option value="">-선택-</option> <option value="">-선택-</option>
<?php foreach ($codes as $c): ?> <?php foreach (($codes['STEP_VERIFICATION']['items'] ?? []) as $cd => $cdNm): ?>
<?php if ($c['category'] === "STEP_VERIFICATION"): ?> <option value="<?= $cd ?>"><?= $cdNm ?></option>
<option value="<?= $c['cd'] ?>"><?= $c['cd_nm'] ?></option>
<?php endif; ?>
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
</div> </div>
@@ -175,10 +132,8 @@
<div class="d-flex gap-2"> <div class="d-flex gap-2">
<select name="vrfcreq_way" id="vrfcreq_way" class="form-select form-select-sm"> <select name="vrfcreq_way" id="vrfcreq_way" class="form-select form-select-sm">
<option value="">-검증방식-</option> <option value="">-검증방식-</option>
<?php foreach ($codes as $c): ?> <?php foreach (($codes['VRFCREQ_WAY']['items'] ?? []) as $cd => $cdNm): ?>
<?php if ($c['category'] === "VRFCREQ_WAY"): ?> <option value="<?= $cd ?>"><?= $cdNm ?></option>
<option value="<?= $c['cd'] ?>"><?= $c['cd_nm'] ?></option>
<?php endif; ?>
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
<select name="vrfc_type_sub" id="vrfc_type_sub" class="form-select form-select-sm"> <select name="vrfc_type_sub" id="vrfc_type_sub" class="form-select form-select-sm">
@@ -192,10 +147,8 @@
<label class="form-label mb-1">매체사</label> <label class="form-label mb-1">매체사</label>
<select name="rcpt_cpid" class="form-select form-select-sm"> <select name="rcpt_cpid" class="form-select form-select-sm">
<option value="">-전체-</option> <option value="">-전체-</option>
<?php foreach ($codes as $c): ?> <?php foreach (($codes['CP_ID']['items'] ?? []) as $cd => $cdNm): ?>
<?php if ($c['category'] === "CP_ID"): ?> <option value="<?= $cd ?>"><?= $cdNm ?></option>
<option value="<?= $c['cd'] ?>"><?= $c['cd_nm'] ?></option>
<?php endif; ?>
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
</div> </div>
@@ -205,10 +158,8 @@
<label class="form-label mb-1">매물종류</label> <label class="form-label mb-1">매물종류</label>
<select name="rlet_type_cd" class="form-select form-select-sm"> <select name="rlet_type_cd" class="form-select form-select-sm">
<option value="">-매물종류-</option> <option value="">-매물종류-</option>
<?php foreach ($codes as $c): ?> <?php foreach (($codes['ARTICLE_TYPE']['items'] ?? []) as $cd => $cdNm): ?>
<?php if ($c['category'] === "ARTICLE_TYPE"): ?> <option value="<?= $cd ?>"><?= $cdNm ?></option>
<option value="<?= $c['cd'] ?>"><?= $c['cd_nm'] ?></option>
<?php endif; ?>
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
</div> </div>
@@ -246,23 +197,8 @@
</form> </form>
</div> </div>
</div> </div>
</div>
<div class="col-md-12 col-xl-12">
<div class="main-card mb-3 card"> <div class="main-card mb-3 card">
<div class="card-header d-flex align-items-center">
<div class="d-flex align-items-center flex-wrap" style="gap: 8px; flex: 1">
</div>
<div class="ml-auto">
<button class="btn btn-sm btn-outline-success" id="excel-download">
<i class="fa fa-fw" aria-hidden="true" title="file-excel-o"></i>
엑셀다운로드
</button>
</div>
</div>
<div class="card-body"> <div class="card-body">
<table id="resultList" class="table table-hover table-striped table-bordered"> <table id="resultList" class="table table-hover table-striped table-bordered">
<thead> <thead>
@@ -290,11 +226,18 @@
</div> </div>
</div> </div>
<?= $this->endSection() ?>
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.13.6/css/jquery.dataTables.min.css" /> <?= $this->section('page_styles') ?>
<?= $this->include('layouts/partials/datatables_css') ?>
<link href="https://unpkg.com/dropzone@6.0.0-beta.1/dist/dropzone.css" rel="stylesheet" type="text/css" /> <link href="https://unpkg.com/dropzone@6.0.0-beta.1/dist/dropzone.css" rel="stylesheet" type="text/css" />
<script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script> <style>
<script defer src="/architectui/assets/js/datatable.kor.js"></script> #resultList_wrapper .dt-start { display: flex; align-items: center; gap: 1rem; align-items: baseline;}
</style>
<?= $this->endSection() ?>
<?= $this->section('page_scripts') ?>
<?= $this->include('layouts/partials/datatables_js') ?>
<script type="text/javascript" src="https://oapi.map.naver.com/openapi/v3/maps.js?ncpKeyId=dtounkwjc5"></script> <script type="text/javascript" src="https://oapi.map.naver.com/openapi/v3/maps.js?ncpKeyId=dtounkwjc5"></script>
<script type="text/javascript"> <script type="text/javascript">
@@ -305,7 +248,38 @@
var table; var table;
$(function () { // 검색 조건 저장
function saveSearchForm() {
const data = $("#frm_srch_info").serializeArray();
localStorage.setItem("m701_search", JSON.stringify(data));
}
// 검색 조건 복원
function restoreSearchForm() {
const saved = localStorage.getItem("m701_search");
if (!saved) return;
const data = JSON.parse(saved);
data.forEach(function(item) {
$("[name='" + item.name + "']").val(item.value);
});
// 주요 select에 대해 change 이벤트 트리거
$("#srcSido, #srcGugun, #bonbu, #team, #vrfcreq_way").trigger("change");
}
$(function () {
// referrer에 '/m701'가 없으면 검색 조건 초기화
if (!document.referrer.includes('/m701')) {
localStorage.removeItem("m701_search");
}
restoreSearchForm();
// Bootstrap Tooltip 초기화 (빠른 표시)
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl, {
delay: { "show": 100, "hide": 100 }
})
})
$("#bonbu").on("change", function (e) { $("#bonbu").on("change", function (e) {
@@ -479,6 +453,24 @@
initReceiptDate(); initReceiptDate();
table = $('#resultList').DataTable({ table = $('#resultList').DataTable({
layout: {
topStart: 'pageLength', // 좌측 상단(페이지당 개수) 제거 (필요시)
topEnd: function () {
// 직접 작성하신 버튼 HTML 구조를 생성
let btn = document.createElement('button');
btn.id = 'excel-download';
btn.className = 'btn btn-sm btn-outline-success';
btn.innerHTML = '<i class="fa fa-fw fa-file-excel-o" aria-hidden="true"></i> 엑셀다운로드';
// 클릭 이벤트 연결
btn.addEventListener('click', function() {
console.log('엑셀 다운로드 실행');
// 여기에 다운로드 로직 추가
});
return btn;
}
},
language: lang_kor, language: lang_kor,
serverSide: true, serverSide: true,
processing: true, processing: true,
@@ -526,15 +518,17 @@
{ 'targets': '_all', "defaultContent": "" }, { 'targets': '_all', "defaultContent": "" },
], ],
columns: [ columns: [
{ data: 'atcl_no' }, { data: 'atcl_no' , render: function (data, type, row) {
{ data: 'pre_stat' }, return `<a href='<?php echo site_url('m701/m701a/detail') ?>/${row.vr_sq}' class='text-decoration-none'>${data}</a>`;
{ data: 'insert_tm' }, }, className: 'tw-90' },
{ data: 'vrfc_type' }, { data: 'pre_stat', className: 'tw-120' },
{ data: null, render: fn_region_render }, { data: 'insert_tm', className: 'tw-130' },
{ data: null, render: fn_addr_render }, { data: 'vrfc_type', className: 'tw-100' },
{ data: 'cpid' }, { data: null, render: fn_region_render , className: 'tw-200' },
{ data: 'realtor_nm' }, { data: null, render: fn_addr_render , className: 'tw-180' },
{ data: 'usr_nm', width: "80px" }, { data: 'cpid', className:'tw-80' },
{ data: 'realtor_nm' , className: 'tw-150' },
{ data: 'usr_nm', className: 'tw-80' },
{ data: null, render: fn_tm_render }, { data: null, render: fn_tm_render },
{ data: 'reg_charger' }, { data: 'reg_charger' },
{ data: 'rgbk_check_tm' }, { data: 'rgbk_check_tm' },
@@ -545,20 +539,28 @@
paging: true, paging: true,
searching: false, searching: false,
ordering: false, ordering: false,
autoWidth: false,
}); });
$('#resultList tbody').on('click', 'tr', function (e) { $('#resultList tbody').on('click', 'tr', function (e) {
if ($(e.target).closest('td.dt-no-rowclick').length) return; if ($(e.target).closest('td.dt-no-rowclick').length) return;
const rowData = table.row(this).data(); const rowData = table.row(this).data();
if (!rowData) return; if (!rowData) return;
const vr_sq = rowData.vr_sq; const vr_sq = rowData.vr_sq;
window.open("<?= site_url('m701/m701a/detail') ?>/" + vr_sq, '_blank'); const url = "<?= site_url('m701/m701a/detail') ?>/" + vr_sq;
// Ctrl+Click 또는 마우스 중간(휠) 클릭(e.button === 1) 시 새탭
if (e.ctrlKey || e.button === 1) {
window.open(url, '_blank');
return;
}
// 일반 클릭은 현재 탭
window.location.href = url;
}); });
$('#btnSearch').on('click', function () { $('#btnSearch').on('click', function () {
table.ajax.reload() saveSearchForm();
table.ajax.reload();
}); });
// 엑셀 다운로드 click // 엑셀 다운로드 click

View File

@@ -54,6 +54,10 @@ if (!empty($data['cert_register']) && $data['cert_register_save_yn'] != 'Y') { /
text-align: left; text-align: left;
} }
.tbl_basic2 td {
text-align: left;
}
.blockUI { .blockUI {
z-index: 1500 !important; z-index: 1500 !important;
} }
@@ -65,18 +69,16 @@ if (!empty($data['cert_register']) && $data['cert_register_save_yn'] != 'Y') { /
max-width: 180px; max-width: 180px;
} }
.card-header {
display: flex !important;
align-items: center;
}
.card-header-tab {
justify-content: flex-start !important;
}
.table-scroll { .table-scroll {
max-height: 300px; max-height: 300px;
overflow-y: scroll; overflow-y: scroll;
min-height: 100px;
}
/* 정보변경 이력 테이블 깜박임 방지 */
.apt-info-table {
table-layout: fixed;
width: 100%;
} }
.swal2-cancel { .swal2-cancel {
@@ -104,14 +106,6 @@ if (!empty($data['cert_register']) && $data['cert_register_save_yn'] != 'Y') { /
} }
</style> </style>
<div class="app-page-title">
<div class="page-title-wrapper">
<div class="page-title-heading">
<div>배정매물 상세 내용</div>
</div>
</div>
</div>
<form name="rcptFrm" id="rcptFrm" method="post" action="" enctype="multipart/form-data" autocomplete="off" <form name="rcptFrm" id="rcptFrm" method="post" action="" enctype="multipart/form-data" autocomplete="off"
onsubmit="return false;"> onsubmit="return false;">
<input type="hidden" name="address_code" id="address_code" value="<?= $data['address_code'] ?>" /> <input type="hidden" name="address_code" id="address_code" value="<?= $data['address_code'] ?>" />
@@ -124,31 +118,19 @@ if (!empty($data['cert_register']) && $data['cert_register_save_yn'] != 'Y') { /
<div class="col-md-12 col-xl-12"> <div class="col-md-12 col-xl-12">
<div class="col-lg-12"> <div class="col-lg-12">
<div class="main-card mb-3 card"> <div class="main-card mb-2 card">
<div class="card-header" style="width:100%; max-width:100%; min-width:600px; padding:0; border:0;"> <div class="card-header bg-white border-bottom shadow-sm">
<p class="left"> <div class="d-flex flex-wrap align-items-center w-100 justify-content-between card-header-tab">
</p> <h4 class="mb-0 fw-bold text-dark">배정매물 상세 내용</h4>
<table style="width:100%; min-width:600px; padding:0; border:0;" cellpadding="0" cellspacing="0" <div class="d-flex align-items-center flex-nowrap gap-4 ms-auto" style="white-space:nowrap;">
border="0" width="100%"> <span class="text-muted me-2">매물ID:</span>
<tbody> <span class="fw-bold text-primary fs-6 me-4"> <?= $data['atcl_no'] ?> </span>
<tr> <span class="text-muted me-2">CP ID:</span>
<td style="width: 50%;padding-left: 20px"><span class="tit">매물ID :</span> <span <span class="fw-bold text-primary fs-6 me-4"> <?= $data['cpid'] ?> </span>
class="num"><?= $data['atcl_no'] ?></span> <span class="text-muted me-2">현재 상태:</span>
</td> <span class="fw-bold text-danger fs-6"> <?= $data['pre_stat'] ?> </span>
<td style="width: 20%;"><span class="tit">CP ID :</span> <span </div>
class="num"><?= $data['cpid'] ?></span></td> </div>
<td style="width: 30%; text-align: right;padding-right: 20px"><span class="tit">현재 상태
:</span> <span class="num"><?= $data['pre_stat'] ?></span></td>
</tr>
<tr>
<td height="15"></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
<p></p>
</div> </div>
<div class="card-body"> <div class="card-body">
<h5 class="card-title">공인 중개사 정보</h5> <h5 class="card-title">공인 중개사 정보</h5>
@@ -170,16 +152,16 @@ if (!empty($data['cert_register']) && $data['cert_register_save_yn'] != 'Y') { /
</div> </div>
</div> </div>
<div class="main-card mb-3 card"> <div class="main-card mb-2 card">
<div class="card-body"> <div class="card-body">
<h5 class="card-title">매물 정보</h5> <h5 class="card-title">매물 정보</h5>
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-bordered table-sm tbl_basic2 align-middle mb-0 apt-info-table"> <table class="table table-bordered table-sm tbl_basic2 align-middle mb-0 apt-info-table">
<colgroup> <colgroup>
<col style="width:120px"> <col width="15%" />
<col style="width:320px"> <col width="35%" />
<col style="width:120px"> <col width="15%" />
<col style="width:320px"> <col width="35%" />
</colgroup> </colgroup>
<tbody> <tbody>
@@ -523,7 +505,7 @@ if (!empty($data['cert_register']) && $data['cert_register_save_yn'] != 'Y') { /
</div> </div>
<div class="main-card mb-3 card" id="docu_chk" style="display: none;"> <div class="main-card mb-2 card" id="docu_chk" style="display: none;">
<div class="card-body"> <div class="card-body">
<h5 class="card-title">서류확인 정보</h5> <h5 class="card-title">서류확인 정보</h5>
@@ -766,7 +748,7 @@ if (!empty($data['cert_register']) && $data['cert_register_save_yn'] != 'Y') { /
</div> </div>
</div> </div>
<div class="main-card mb-3 card" id="call_chk" style="display: none;"> <div class="main-card mb-2 card" id="call_chk" style="display: none;">
<div class="card-body"> <div class="card-body">
<h5 class="card-title">전화확인 정보</h5> <h5 class="card-title">전화확인 정보</h5>
@@ -940,7 +922,7 @@ if (!empty($data['cert_register']) && $data['cert_register_save_yn'] != 'Y') { /
<tr> <tr>
<th>메모</th> <th>메모</th>
<td> <td>
<div class="d-flex flex-column gap-1" style=""> <div class="d-flex flex-column gap-1">
<textarea class="form-control" name="memo_tel" id="memo_tel" rows="2" <textarea class="form-control" name="memo_tel" id="memo_tel" rows="2"
style="resize: none;"><?= $memo['memo'] ?></textarea> style="resize: none;"><?= $memo['memo'] ?></textarea>
@@ -976,7 +958,7 @@ if (!empty($data['cert_register']) && $data['cert_register_save_yn'] != 'Y') { /
</div> </div>
</div> </div>
<div class="main-card mb-3 card" id="regibox"> <div class="main-card mb-2 card" id="regibox">
<div class="card-body"> <div class="card-body">
<h5 class="card-title">등기부등본확인 정보</h5> <h5 class="card-title">등기부등본확인 정보</h5>
@@ -1188,7 +1170,7 @@ if (!empty($data['cert_register']) && $data['cert_register_save_yn'] != 'Y') { /
</div> </div>
<div class="main-card mb-3 card"> <div class="main-card mb-2 card">
<div class="card-body "> <div class="card-body ">
<h5 class="card-title">정보변경 이력</h5> <h5 class="card-title">정보변경 이력</h5>
<div class="table-scroll"> <div class="table-scroll">
@@ -1216,7 +1198,7 @@ if (!empty($data['cert_register']) && $data['cert_register_save_yn'] != 'Y') { /
</div> </div>
</div> </div>
<div class="main-card mb-3 card"> <div class="main-card mb-2 card">
<button type="button" class="btn btn-light btn-sm"> <button type="button" class="btn btn-light btn-sm">
목록 목록
</button> </button>

View File

@@ -1,76 +1,33 @@
<?= $this->extend('layouts/main') ?> <?= $this->extend('layouts/main') ?>
<?= $this->section('content') ?> <?= $this->section('content') ?>
<style>
th {
font-size: 11px;
text-align: center;
}
td {
text-align: center;
}
#resultList tbody tr {
cursor: pointer;
font-size: 12px;
}
.blockUI {
z-index: 1500 !important;
}
.ellipsis {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 180px;
}
.card-header {
display: flex !important;
align-items: center;
}
.card-header-tab {
justify-content: flex-start !important;
}
.table-scroll {
max-height: 300px;
overflow-y: scroll;
}
.swal2-cancel {
background-color: #ff0000 !important;
color: #fff !important;
}
</style>
<h1>확인매물 현황</h1>
<div class="col-md-12 col-xl-12"> <div class="col-md-12 col-xl-12">
<div class="main-card mb-3 card"> <div class="main-card mb-3 card">
<div class="card-header bg-white border-bottom shadow-sm">
<div class="d-flex flex-wrap align-items-center gap-3 card-header-tab">
<div>
<h4 class="mb-0 fw-bold text-dark">배정매물 현황</h4>
</div>
</div>
</div>
<div class="card-body"> <div class="card-body">
<form id="frm_srch_info" method="get" onsubmit="return false;"> <form id="frm_srch_info" method="get" onsubmit="return false;">
<input type="hidden" name="m" id="m" value="M801" /> <input type="hidden" name="m" id="m" value="M801" />
<input type="hidden" name="todo" id="todo" value="inq" /> <input type="hidden" name="todo" id="todo" value="inq" />
<input type="hidden" name="usr_id" value="" /> <input type="hidden" name="usr_id" value="" />
<!-- 안내 -->
<div class="alert alert-warning py-2 mb-3">
<small class="mb-0">
매물번호를 입력하면 <b>다른 조건은 무시</b>됩니다.
</small>
</div>
<!-- 검색 폼 --> <!-- 검색 폼 -->
<div class="row g-3"> <div class="row g-3">
<!-- 매물번호 --> <!-- 매물번호 -->
<div class="col-md-1"> <div class="col-md-1">
<label class="form-label mb-1">매물번호</label> <label class="form-label mb-1">
매물번호
<i class="pe-7s-info info-tooltip" data-bs-toggle="tooltip" data-bs-placement="top"
title="매물번호를 입력하면 다른 조건은 무시됩니다"></i>
</label>
<input type="text" name="atcl_no" class="form-control form-control-sm" placeholder="매물번호" maxlength="10" <input type="text" name="atcl_no" class="form-control form-control-sm" placeholder="매물번호" maxlength="10"
data-bs-toggle="tooltip" data-bs-placement="top" title="매물번호를 입력하면 다른 조건은 무시됩니다"
onkeypress="atcl_no_enter(event)"> onkeypress="atcl_no_enter(event)">
</div> </div>
@@ -79,10 +36,8 @@
<label class="form-label mb-1">현재상태</label> <label class="form-label mb-1">현재상태</label>
<select name="stat_cd" class="form-select form-select-sm"> <select name="stat_cd" class="form-select form-select-sm">
<option value="">-선택-</option> <option value="">-선택-</option>
<?php foreach ($codes as $c): ?> <?php foreach (($codes['STEP_VERIFICATION']['items'] ?? []) as $cd => $cdNm): ?>
<?php if ($c['category'] === "STEP_VERIFICATION"): ?> <option value="<?= $cd ?>"><?= $cdNm ?></option>
<option value="<?= $c['cd'] ?>"><?= $c['cd_nm'] ?></option>
<?php endif; ?>
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
</div> </div>
@@ -175,10 +130,8 @@
<div class="d-flex gap-2"> <div class="d-flex gap-2">
<select name="vrfcreq_way" id="vrfcreq_way" class="form-select form-select-sm"> <select name="vrfcreq_way" id="vrfcreq_way" class="form-select form-select-sm">
<option value="">-검증방식-</option> <option value="">-검증방식-</option>
<?php foreach ($codes as $c): ?> <?php foreach (($codes['VRFCREQ_WAY']['items'] ?? []) as $cd => $cdNm): ?>
<?php if ($c['category'] === "VRFCREQ_WAY"): ?> <option value="<?= $cd ?>"><?= $cdNm ?></option>
<option value="<?= $c['cd'] ?>"><?= $c['cd_nm'] ?></option>
<?php endif; ?>
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
<select name="vrfc_type_sub" id="vrfc_type_sub" class="form-select form-select-sm"> <select name="vrfc_type_sub" id="vrfc_type_sub" class="form-select form-select-sm">
@@ -192,10 +145,8 @@
<label class="form-label mb-1">매체사</label> <label class="form-label mb-1">매체사</label>
<select name="rcpt_cpid" class="form-select form-select-sm"> <select name="rcpt_cpid" class="form-select form-select-sm">
<option value="">-전체-</option> <option value="">-전체-</option>
<?php foreach ($codes as $c): ?> <?php foreach (($codes['CP_ID']['items'] ?? []) as $cd => $cdNm): ?>
<?php if ($c['category'] === "CP_ID"): ?> <option value="<?= $cd ?>"><?= $cdNm ?></option>
<option value="<?= $c['cd'] ?>"><?= $c['cd_nm'] ?></option>
<?php endif; ?>
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
</div> </div>
@@ -205,10 +156,8 @@
<label class="form-label mb-1">매물종류</label> <label class="form-label mb-1">매물종류</label>
<select name="rlet_type_cd" class="form-select form-select-sm"> <select name="rlet_type_cd" class="form-select form-select-sm">
<option value="">-매물종류-</option> <option value="">-매물종류-</option>
<?php foreach ($codes as $c): ?> <?php foreach (($codes['ARTICLE_TYPE']['items'] ?? []) as $cd => $cdNm): ?>
<?php if ($c['category'] === "ARTICLE_TYPE"): ?> <option value="<?= $cd ?>"><?= $cdNm ?></option>
<option value="<?= $c['cd'] ?>"><?= $c['cd_nm'] ?></option>
<?php endif; ?>
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
</div> </div>
@@ -240,50 +189,6 @@
<div class="col-md-12 col-xl-12"> <div class="col-md-12 col-xl-12">
<div class="main-card mb-3 card"> <div class="main-card mb-3 card">
<div class="card-header d-flex align-items-center">
<div class="d-flex align-items-center flex-wrap" style="gap:8px; flex:1;">
<select class="form-control form-control-sm" id="damdangT" style="width:150px;">
<option value="1">전화/서류 담당자</option>
<option value="2">등기부등본 담당자</option>
</select>
<select class="form-control form-control-sm" id="bonbu2" style="width:140px;">
<option value="">-본부-</option>
<?php foreach ($bonbu as $d): ?>
<option value="<?= $d['dept_sq'] ?>"><?= $d['dept_nm'] ?></option>
<?php endforeach; ?>
</select>
<select class="form-control form-control-sm" id="team2" style="width:160px;">
<option value="">-팀-</option>
</select>
<select class="form-control form-control-sm" id="damdang2" style="width:160px;">
<option value="">-담당자-</option>
</select>
<button type="button" class="btn btn-sm btn-outline-light" id="btn_part_change">
배정변경
</button>
<button type="button" class="btn btn-sm btn-outline-light" id="btn_part_omit">
서류누락
</button>
</div>
<div class="ml-auto">
<button class="btn btn-sm btn-outline-light" id="excel-download">
등기부등본 전송
</button>
<button class="btn btn-sm btn-outline-success" id="excel-download">
<i class="fa fa-fw" aria-hidden="true" title="file-excel-o"></i>
엑셀다운로드
</button>
</div>
</div>
<div class="card-body"> <div class="card-body">
<table id="resultList" class="table table-hover table-striped table-bordered"> <table id="resultList" class="table table-hover table-striped table-bordered">
<thead> <thead>
@@ -313,11 +218,19 @@
</div> </div>
</div> </div>
<?= $this->endSection() ?>
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.13.6/css/jquery.dataTables.min.css" /> <?= $this->section('page_styles') ?>
<?= $this->include('layouts/partials/datatables_css') ?>
<link href="https://unpkg.com/dropzone@6.0.0-beta.1/dist/dropzone.css" rel="stylesheet" type="text/css" /> <link href="https://unpkg.com/dropzone@6.0.0-beta.1/dist/dropzone.css" rel="stylesheet" type="text/css" />
<script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script> <style>
<script defer src="/architectui/assets/js/datatable.kor.js"></script> #resultList_wrapper .dt-start { display: flex; align-items: center; gap: 1rem; align-items: baseline;}
</style>
<?= $this->endSection() ?>
<?= $this->section('page_scripts') ?>
<?= $this->include('layouts/partials/datatables_js') ?>
<?= $this->include('layouts/partials/datatables_v2_layout_helpers') ?>
<script type="text/javascript" src="https://oapi.map.naver.com/openapi/v3/maps.js?ncpKeyId=dtounkwjc5"></script> <script type="text/javascript" src="https://oapi.map.naver.com/openapi/v3/maps.js?ncpKeyId=dtounkwjc5"></script>
<script type="text/javascript"> <script type="text/javascript">
@@ -328,7 +241,30 @@
var table; var table;
// 검색 조건 저장
function saveSearchForm() {
const data = $("#frm_srch_info").serializeArray();
localStorage.setItem("m702_search", JSON.stringify(data));
}
// 검색 조건 복원
function restoreSearchForm() {
const saved = localStorage.getItem("m702_search");
if (!saved) return;
const data = JSON.parse(saved);
data.forEach(function(item) {
$("[name='" + item.name + "']").val(item.value);
});
// 주요 select에 대해 change 이벤트 트리거
$("#srcSido, #srcGugun, #bonbu, #team, #vrfcreq_way").trigger("change");
}
$(function () { $(function () {
// referrer에 '/m702'가 없으면 검색 조건 초기화
if (!document.referrer.includes('/m702')) {
localStorage.removeItem("m702_search");
}
restoreSearchForm();
$("#bonbu").on("change", function (e) { $("#bonbu").on("change", function (e) {
@@ -419,7 +355,7 @@
}); });
$("#bonbu, #team, #bonbu2, #team2").on("change", function (e) { $(document).on("change", "#bonbu, #team, #bonbu2, #team2", function (e) {
const targetId = this.id; const targetId = this.id;
@@ -505,6 +441,69 @@
initReceiptDate(); initReceiptDate();
table = $('#resultList').DataTable({ table = $('#resultList').DataTable({
layout: {
topStart: function() {
const container = document.createElement('div');
container.className = 'd-flex align-items-center flex-wrap';
container.style.cssText = 'gap:8px; flex:1;';
// 담당자 유형
const damdangT = document.createElement('select');
damdangT.id = 'damdangT';
damdangT.className = 'form-control form-control-sm';
damdangT.style.width = '150px';
damdangT.innerHTML = `
<option value="1">전화/서류 담당자</option>
<option value="2">등기부등본 담당자</option>
`;
// 본부
const bonbu2 = document.createElement('select');
bonbu2.id = 'bonbu2';
bonbu2.className = 'form-control form-control-sm';
bonbu2.style.width = '140px';
bonbu2.innerHTML = '<option value="">-본부-</option>';
// bonbuArr 배열을 사용하여 옵션 추가
bonbuArr.forEach(function(d) {
const opt = document.createElement('option');
opt.value = d.dept_sq;
opt.textContent = d.dept_nm;
bonbu2.appendChild(opt);
});
// 팀
const team2 = document.createElement('select');
team2.id = 'team2';
team2.className = 'form-control form-control-sm';
team2.style.width = '160px';
team2.innerHTML = '<option value="">-팀-</option>';
// 담당자
const damdang2 = document.createElement('select');
damdang2.id = 'damdang2';
damdang2.className = 'form-control form-control-sm';
damdang2.style.width = '160px';
damdang2.innerHTML = '<option value="">-담당자-</option>';
// 배정변경 버튼
const btnChange = document.createElement('button');
btnChange.type = 'button';
btnChange.id = 'btn_part_change';
btnChange.className = 'btn btn-sm btn-outline-light';
btnChange.textContent = '배정변경';
// 서류누락 버튼
const btnOmit = document.createElement('button');
btnOmit.type = 'button';
btnOmit.id = 'btn_part_omit';
btnOmit.className = 'btn btn-sm btn-outline-light';
btnOmit.textContent = '서류누락';
container.append(damdangT, bonbu2, team2, damdang2, btnChange, btnOmit);
return container;
},
topEnd: v2TopEndButtons({ showSendButton: true })
},
language: lang_kor, language: lang_kor,
serverSide: true, serverSide: true,
processing: true, processing: true,
@@ -550,13 +549,15 @@
{ 'targets': '_all', "defaultContent": "" }, { 'targets': '_all', "defaultContent": "" },
], ],
columns: [ columns: [
{ data: null, render: fn_chk_render, width: "50px", className: "dt-no-rowclick" }, { data: null, render: fn_chk_render, className: "dt-no-rowclick tw-50" },
{ data: 'atcl_no' }, { data: 'atcl_no' , function(data, type, row) {
return `<a href='<?php echo site_url('m702/m702a/detail') ?>/${row.vr_sq}' class='text-decoration-none'>${data}</a>`;
}, className:'tw-90' },
{ data: 'pre_stat' }, { data: 'pre_stat' },
{ data: 'insert_tm' }, { data: 'insert_tm' },
{ data: 'vrfc_type' }, { data: 'vrfc_type' },
{ data: null, render: fn_region_render }, { data: null, render: fn_region_render , className: 'tw-200' },
{ data: null, render: fn_addr_render }, { data: null, render: fn_addr_render , className: 'tw-180' },
{ data: 'cpid' }, { data: 'cpid' },
{ data: 'realtor_nm' }, { data: 'realtor_nm' },
{ data: 'usr_nm', width: "80px" }, { data: 'usr_nm', width: "80px" },
@@ -582,6 +583,7 @@
}); });
$('#btnSearch').on('click', function () { $('#btnSearch').on('click', function () {
saveSearchForm();
table.ajax.reload() table.ajax.reload()
}); });

View File

@@ -1,6 +1,6 @@
<?= $this->extend('layouts/main') ?> <?= $this->extend('layouts/main') ?>
<?= $this->section('content') ?> <?= $this->section('content') ?>
<link rel="stylesheet" href="/common/css/custom.css">
<style> <style>
table th { table th {
vertical-align: middle; vertical-align: middle;
@@ -20,43 +20,68 @@
text-align: left; text-align: left;
} }
.blockUI {
z-index: 1500 !important;
}
.ellipsis {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 180px;
}
/* GLightbox z-index 설정 */
.glightbox-container {
z-index: 9999 !important;
}
.table-scroll {
max-height: 300px;
overflow-y: scroll;
min-height: 100px;
}
.swal2-cancel {
background-color: #ff0000 !important;
color: #fff !important;
}
.num { .num {
font-family: Tahoma;
color: #b68556; color: #b68556;
font-size: 17px; }
.table th,
.table td {
vertical-align: top;
box-sizing: border-box;
} }
</style> </style>
<div class="app-page-title"> <div class="col-md-12 col-xl-12">
<div class="page-title-wrapper"> <div class="col-lg-12">
<div class="page-title-heading"> <div class="main-card mb-2 card">
<div>홍보확인서 상세 내용</div> <div class="card-header bg-white border-bottom shadow-sm">
</div> <div class="d-flex flex-wrap align-items-center w-100 justify-content-between card-header-tab">
</div> <h4 class="mb-0 fw-bold text-dark">홍보확인서 상세 내용</h4>
</div> <div class="d-flex align-items-center flex-nowrap gap-4 ms-auto" style="white-space:nowrap;">
<?php if (($data['receiver'] ?? '') != "API"): ?>
<div class="row"> <span class="text-muted me-2">발신번호:</span>
<div class="col-12"> <span class="fw-bold text-primary fs-6 me-4"><?= esc(str_replace('-', '', $data['caller_no'] ?? '')) ?></span>
<div class="main-card mb-3 card"> <?php endif; ?>
<span class="text-muted me-2">팩스 순번:</span>
<span class="fw-bold text-primary fs-6 me-4"><?= $data['fax_sq'] ?></span>
<?php if (($data['receiver'] ?? '') != "API"): ?> <?php if (($data['receiver'] ?? '') != "API"): ?>
<div class="card-header">
<div class="d-flex align-items-center w-100">
<div class="ms-auto d-flex gap-1">
<span class="text-muted small me-2">
발신번호 : <?= esc(str_replace('-', '', $data['caller_no'] ?? '')) ?>
</span>
<button type="button" class="btn btn-sm btn-outline-secondary" <button type="button" class="btn btn-sm btn-outline-secondary"
onclick="faximage_rotate(90)">90˚</button> onclick="faximage_rotate(90)">90˚</button>
<button type="button" class="btn btn-sm btn-outline-secondary" <button type="button" class="btn btn-sm btn-outline-secondary"
onclick="faximage_rotate(180)">180˚</button> onclick="faximage_rotate(180)">180˚</button>
<button type="button" class="btn btn-sm btn-outline-secondary" <button type="button" class="btn btn-sm btn-outline-secondary"
onclick="faximage_rotate(270)">270˚</button> onclick="faximage_rotate(270)">270˚</button>
</div>
</div>
</div>
<?php endif; ?> <?php endif; ?>
</div>
</div>
</div>
<div class="card-body"> <div class="card-body">
<!-- table 유지 + 반응형 --> <!-- table 유지 + 반응형 -->
<div class="table-responsive"> <div class="table-responsive">
@@ -92,13 +117,15 @@
?> ?>
<?php if ($ext === 'pdf'): ?> <?php if ($ext === 'pdf'): ?>
<a href="<?= esc($filePath . $fileName) ?>" class="embed"></a> <a href="<?= esc($filePath . $fileName) ?>" target="_blank" class="btn btn-primary">PDF 보기</a>
<?php else: ?> <?php else: ?>
<a onclick="fn_preview('<?= esc($image_path . $fileName) ?>?<?= esc(($data['img_width'] ?? '') . ($data['img_height'] ?? '')) ?>')" <a href="<?= esc($image_path . $fileName) ?>?<?= esc(($data['img_width'] ?? '') . ($data['img_height'] ?? '')) ?>"
rel="lightbox"> class="glightbox"
data-gallery="fax-image"
data-title="팩스 이미지">
<img id="fax_image" <img id="fax_image"
src="<?= esc($image_path . $fileName) ?>?<?= esc(($data['img_width'] ?? '') . ($data['img_height'] ?? '')) ?>" src="<?= esc($image_path . $fileName) ?>?<?= esc(($data['img_width'] ?? '') . ($data['img_height'] ?? '')) ?>"
alt="fax" class="img-fluid" style="width: 100%;" /> alt="fax" class="img-fluid" style="width: 100%; cursor: pointer;" />
</a> </a>
<?php endif; ?> <?php endif; ?>
</td> </td>
@@ -121,7 +148,7 @@
<div class="card-body p-2"> <div class="card-body p-2">
<table class="table table-sm table-bordered mb-2 tbl_basic2 align-middle"> <table class="table table-sm table-bordered mb-2 tbl_basic2 align-middle">
<colgroup> <colgroup>
<col style="width: 110px;"> <col style="width: 120px;">
<col> <col>
</colgroup> </colgroup>
<tbody> <tbody>
@@ -652,7 +679,7 @@
<table <table
class="table table-sm table-bordered mb-2 tbl_basic2 align-middle"> class="table table-sm table-bordered mb-2 tbl_basic2 align-middle">
<colgroup> <colgroup>
<col style="width: 140px;"> <col style="width: 120px;">
<col> <col>
</colgroup> </colgroup>
<tbody> <tbody>
@@ -940,10 +967,10 @@
<div class="table-scroll" style="height: 250px;overflow-y: scroll;"> <div class="table-scroll" style="height: 250px;overflow-y: scroll;">
<table class="table table-bordered table-sm tbl_basic2 apt-info-table"> <table class="table table-bordered table-sm tbl_basic2 apt-info-table">
<tr> <tr>
<th width="90" style="text-align: center;">진행상태</th> <th width="150" style="text-align: center;">진행상태</th>
<th width="150" style="text-align: center;">변경내용</th> <th width="150" style="text-align: center;">변경내용</th>
<th width="90" style="text-align: center;">처리자(ID)</th> <th width="90" style="text-align: center;">처리자(ID)</th>
<th width="120" style="text-align: center;">처리일시</th> <th width="150" style="text-align: center;">처리일시</th>
<th style="text-align: center;">세부내용</th> <th style="text-align: center;">세부내용</th>
</tr> </tr>
<?php if (!empty($history)) { ?> <?php if (!empty($history)) { ?>
@@ -973,29 +1000,25 @@
</div> </div>
</div> </div>
</div> </div>
<?= $this->section('modals') ?>
<div class="modal" id="previewModal" tabindex="-1">
<div class="modal-dialog modal-xl">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">미리보기</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body p-0">
<img id="imgPreview" src="" alt="미리보기" width="100%" height="auto">
</div>
</div>
</div>
</div>
<?= $this->endSection() ?> <?= $this->endSection() ?>
<?= $this->section('page_styles') ?>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/glightbox/dist/css/glightbox.min.css">
<?= $this->endSection() ?>
<?= $this->section('page_scripts') ?>
<script src="https://cdn.jsdelivr.net/npm/glightbox/dist/js/glightbox.min.js"></script>
<script type="text/javascript"> <script type="text/javascript">
$(function () { $(function () {
// GLightbox 초기화
const lightbox = GLightbox({
touchNavigation: true,
loop: false,
autoplayVideos: true,
zoomable: true,
draggable: true
});
}); });
@@ -1776,20 +1799,6 @@
}); });
} }
// 이미지 프리뷰
function fn_preview(src) {
const $img = $('#imgPreview');
// 이미지 표시
$img.attr('src', src).show();
$('#previewTitle').text('이미지 미리보기');
const modal = new bootstrap.Modal(document.getElementById('previewModal'));
modal.show();
}
</script> </script>
<?= $this->endSection() ?> <?= $this->endSection() ?>

View File

@@ -1,76 +1,35 @@
<?= $this->extend('layouts/main') ?> <?= $this->extend('layouts/main') ?>
<?= $this->section('content') ?> <?= $this->section('content') ?>
<style>
th {
font-size: 11px;
text-align: center;
}
td {
text-align: center;
}
#resultList tbody tr {
cursor: pointer;
font-size: 12px;
}
.blockUI {
z-index: 1500 !important;
}
.ellipsis {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 180px;
}
.card-header {
display: flex !important;
align-items: center;
}
.card-header-tab {
justify-content: flex-start !important;
}
.table-scroll {
max-height: 300px;
overflow-y: scroll;
}
.swal2-cancel {
background-color: #ff0000 !important;
color: #fff !important;
}
</style>
<h1>확인매물 현황</h1>
<div class="col-md-12 col-xl-12"> <div class="col-md-12 col-xl-12">
<div class="main-card mb-3 card"> <div class="main-card mb-3 card">
<div class="card-header bg-white border-bottom shadow-sm">
<div class="d-flex flex-wrap align-items-center gap-3 card-header-tab">
<div>
<h4 class="mb-0 fw-bold text-dark">홍보확인서 현황</h4>
</div>
</div>
</div>
<div class="card-body"> <div class="card-body">
<form id="frm_srch_info" method="get" onsubmit="return false;"> <form id="frm_srch_info" method="get" onsubmit="return false;">
<input type="hidden" name="m" id="m" value="M801" /> <input type="hidden" name="m" id="m" value="M801" />
<input type="hidden" name="todo" id="todo" value="inq" /> <input type="hidden" name="todo" id="todo" value="inq" />
<input type="hidden" name="usr_id" value="" /> <input type="hidden" name="usr_id" value="" />
<!-- 안내 -->
<div class="alert alert-warning py-2 mb-3">
<small class="mb-0">
매물번호또는 발신팩스번호를 입력하면 <b>다른 조건은 무시</b>됩니다.
</small>
</div>
<!-- 검색 폼 --> <!-- 검색 폼 -->
<div class="row g-3"> <div class="row g-3">
<!-- 매물번호 --> <!-- 매물번호 -->
<div class="col-md-1"> <div class="col-md-1">
<label class="form-label mb-1">매물번호</label> <label class="form-label mb-1">
매물번호
<i class="pe-7s-info info-tooltip" data-bs-toggle="tooltip" data-bs-placement="top"
title="매물번호 또는 발신팩스번호를 입력하면 다른 조건은 무시됩니다"></i>
</label>
<input type="text" name="atcl_no" class="form-control form-control-sm" placeholder="매물번호" maxlength="10" <input type="text" name="atcl_no" class="form-control form-control-sm" placeholder="매물번호" maxlength="10"
data-bs-toggle="tooltip" data-bs-placement="top" title="매물번호를 입력하면 다른 조건은 무시됩니다"
onkeypress="atcl_no_enter(event)"> onkeypress="atcl_no_enter(event)">
</div> </div>
@@ -94,10 +53,8 @@
<label class="form-label mb-1">현재상태</label> <label class="form-label mb-1">현재상태</label>
<select name="stat_cd" class="form-select form-select-sm"> <select name="stat_cd" class="form-select form-select-sm">
<option value="">-선택-</option> <option value="">-선택-</option>
<?php foreach ($codes as $c): ?> <?php foreach (($codes['STEP_VERIFICATION']['items'] ?? []) as $cd => $cdNm): ?>
<?php if ($c['category'] === "STEP_VERIFICATION"): ?> <option value="<?= $cd ?>"><?= $cdNm ?></option>
<option value="<?= $c['cd'] ?>"><?= $c['cd_nm'] ?></option>
<?php endif; ?>
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
</div> </div>
@@ -200,10 +157,8 @@
<label class="form-label mb-1">매체사</label> <label class="form-label mb-1">매체사</label>
<select name="rcpt_cpid" class="form-select form-select-sm"> <select name="rcpt_cpid" class="form-select form-select-sm">
<option value="">-전체-</option> <option value="">-전체-</option>
<?php foreach ($codes as $c): ?> <?php foreach (($codes['CP_ID']['items'] ?? []) as $cd => $cdNm): ?>
<?php if ($c['category'] === "CP_ID"): ?> <option value="<?= $cd ?>"><?= $cdNm ?></option>
<option value="<?= $c['cd'] ?>"><?= $c['cd_nm'] ?></option>
<?php endif; ?>
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
</div> </div>
@@ -224,10 +179,8 @@
<label class="form-label mb-1">팩스업체</label> <label class="form-label mb-1">팩스업체</label>
<select name="fax_corp" class="form-select form-select-sm"> <select name="fax_corp" class="form-select form-select-sm">
<option value="">-전체-</option> <option value="">-전체-</option>
<?php foreach ($codes as $c): ?> <?php foreach (($codes['FAX_CORP']['items'] ?? []) as $cd => $cdNm): ?>
<?php if ($c['category'] === "FAX_CORP"): ?> <option value="<?= $cd ?>"><?= $cdNm ?></option>
<option value="<?= $c['cd'] ?>"><?= $c['cd_nm'] ?></option>
<?php endif; ?>
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
</div> </div>
@@ -249,21 +202,6 @@
<div class="col-md-12 col-xl-12"> <div class="col-md-12 col-xl-12">
<div class="main-card mb-3 card"> <div class="main-card mb-3 card">
<div class="card-header d-flex align-items-center">
<div class="d-flex align-items-center flex-wrap" style="gap: 8px; flex: 1">
</div>
<div class="ml-auto">
<button class="btn btn-sm btn-outline-success" id="excel-download">
<i class="fa fa-fw" aria-hidden="true" title="file-excel-o"></i>
엑셀다운로드
</button>
</div>
</div>
<div class="card-body"> <div class="card-body">
<table id="resultList" class="table table-hover table-striped table-bordered"> <table id="resultList" class="table table-hover table-striped table-bordered">
<thead> <thead>
@@ -292,11 +230,19 @@
</div> </div>
</div> </div>
<?= $this->endSection() ?>
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.13.6/css/jquery.dataTables.min.css" /> <?= $this->section('page_styles') ?>
<?= $this->include('layouts/partials/datatables_css') ?>
<link href="https://unpkg.com/dropzone@6.0.0-beta.1/dist/dropzone.css" rel="stylesheet" type="text/css" /> <link href="https://unpkg.com/dropzone@6.0.0-beta.1/dist/dropzone.css" rel="stylesheet" type="text/css" />
<script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script> <style>
<script defer src="/architectui/assets/js/datatable.kor.js"></script> #resultList_wrapper .dt-start { display: flex; align-items: center; gap: 1rem; align-items: baseline;}
</style>
<?= $this->endSection() ?>
<?= $this->section('page_scripts') ?>
<?= $this->include('layouts/partials/datatables_js') ?>
<?= $this->include('layouts/partials/datatables_v2_layout_helpers') ?>
<script type="text/javascript" src="https://oapi.map.naver.com/openapi/v3/maps.js?ncpKeyId=dtounkwjc5"></script> <script type="text/javascript" src="https://oapi.map.naver.com/openapi/v3/maps.js?ncpKeyId=dtounkwjc5"></script>
<script type="text/javascript"> <script type="text/javascript">
@@ -308,7 +254,39 @@
var table; var table;
$(function () { // 검색 조건 저장
function saveSearchForm() {
const data = $("#frm_srch_info").serializeArray();
localStorage.setItem("m703_search", JSON.stringify(data));
}
// 검색 조건 복원
function restoreSearchForm() {
const saved = localStorage.getItem("m703_search");
// console.log(saved);
if (!saved) return;
const data = JSON.parse(saved);
data.forEach(function(item) {
$("[name='" + item.name + "']").val(item.value);
});
// 주요 select에 대해 change 이벤트 트리거
$("#srcSido, #srcGugun, #bonbu, #team").trigger("change");
}
$(function () {
// referrer에 '/m703'가 없으면 검색 조건 초기화
if (!document.referrer.includes('/m703')) {
localStorage.removeItem("m703_search");
}
restoreSearchForm();
// Bootstrap Tooltip 초기화 (빠른 표시)
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl, {
delay: { "show": 100, "hide": 100 }
})
})
$("#bonbu").on("change", function (e) { $("#bonbu").on("change", function (e) {
@@ -454,6 +432,12 @@
initReceiptDate(); initReceiptDate();
table = $('#resultList').DataTable({ table = $('#resultList').DataTable({
layout: {
topStart: '',
topEnd: v2TopEndButtons(),
bottomStart: ['pageLength', 'info'],
bottomEnd: 'paging'
},
language: lang_kor, language: lang_kor,
serverSide: true, serverSide: true,
processing: true, processing: true,
@@ -536,7 +520,8 @@
}); });
$('#btnSearch').on('click', function () { $('#btnSearch').on('click', function () {
table.ajax.reload() saveSearchForm();
table.ajax.reload();
}); });
// 엑셀 다운로드 click // 엑셀 다운로드 click
@@ -772,4 +757,6 @@
return str; return str;
} }
</script> </script>
<?= $this->endSection() ?> <?= $this->endSection() ?>

View File

@@ -22,74 +22,6 @@ if (!empty($regist2)) {
<?= $this->extend('layouts/main') ?> <?= $this->extend('layouts/main') ?>
<?= $this->section('content') ?> <?= $this->section('content') ?>
<style>
table th {
vertical-align: middle;
line-height: 1.2;
}
.tbl_basic2 th {
padding: 0 10px;
height: 27px;
line-height: 27px;
vertical-align: middle;
border: solid 1px #d8d9de;
background-color: #eff0f4;
letter-spacing: -1px;
font-weight: normal;
color: #5a5f69;
text-align: left;
}
.blockUI {
z-index: 1500 !important;
}
.ellipsis {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 180px;
}
.card-header {
display: flex !important;
align-items: center;
}
.card-header-tab {
justify-content: flex-start !important;
}
.table-scroll {
max-height: 300px;
overflow-y: scroll;
}
.swal2-cancel {
background-color: #ff0000 !important;
color: #fff !important;
}
.num {
color: #b68556;
font-size: 19px;
}
.table th,
.table td {
vertical-align: top;
box-sizing: border-box;
}
</style>
<div class="app-page-title">
<div class="page-title-wrapper">
<div class="page-title-heading">
<div>전화확인매물 상세 내용</div>
</div>
</div>
</div>
<form name="rcptFrm" id="rcptFrm" method="post" action="" enctype="multipart/form-data" onsubmit="return false;"> <form name="rcptFrm" id="rcptFrm" method="post" action="" enctype="multipart/form-data" onsubmit="return false;">
<input type="hidden" name="address_code" id="address_code" value="<?= $data['address_code'] ?>" /> <input type="hidden" name="address_code" id="address_code" value="<?= $data['address_code'] ?>" />
@@ -102,35 +34,17 @@ if (!empty($regist2)) {
<div class="col-md-12 col-xl-12"> <div class="col-md-12 col-xl-12">
<div class="col-lg-12"> <div class="col-lg-12">
<div class="main-card mb-3 card"> <div class="main-card mb-3 card">
<div class="card-header" style="width:100%; max-width:100%; min-width:600px; padding:0; border:0;"> <div class="card-header bg-white border-bottom shadow-sm">
<p class="left"> <div class="d-flex flex-wrap align-items-center gap-3">
</p> <div class="flex-grow-1">
<table style="width:100%; min-width:600px; padding:0; border:0;" cellpadding="0" cellspacing="0" <h4 class="mb-0 fw-bold text-dark">전화확인매물 상세 내용</h4>
border="0" width="100%"> </div>
<tbody> <div class="d-flex flex-wrap gap-3">
<tr> <div><span class="tit">매물ID:</span> <span class="num"><?= $data['atcl_no'] ?></span></div>
<td style="width: 50%;padding-left: 20px"><span class="tit">매물ID :</span> <span <div><span class="tit">CP ID:</span> <span class="num"><?= $data['cpid'] ?></span></div>
class="num"> <div><span class="tit">현재 상태:</span> <span class="num"><?= $data['pre_stat'] ?></span></div>
<?= $data['atcl_no'] ?> </div>
</span> </div>
</td>
<td style="width: 20%;"><span class="tit">CP ID :</span> <span class="num">
<?= $data['cpid'] ?>
</span></td>
<td style="width: 30%; text-align: right;padding-right: 20px"><span class="tit">현재 상태
:</span> <span class="num">
<?= $data['pre_stat'] ?>
</span></td>
</tr>
<tr>
<td height="15"></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
<p></p>
</div> </div>
<div class="card-body"> <div class="card-body">
<h5 class="card-title">공인 중개사 정보</h5> <h5 class="card-title">공인 중개사 정보</h5>
@@ -548,10 +462,10 @@ if (!empty($regist2)) {
</h5> </h5>
<table class="table table-bordered table-sm tbl_basic2 apt-info-table"> <table class="table table-bordered table-sm tbl_basic2 apt-info-table">
<colgroup> <colgroup>
<col width="15%" /> <col style="width:120px">
<col width="35%" /> <col>
<col width="15%" /> <col style="width:120px">
<col width="35%" /> <col>
</colgroup> </colgroup>
<tr> <tr>
<th>의뢰인(매도자) 전화번호</th> <th>의뢰인(매도자) 전화번호</th>

View File

@@ -1,76 +1,35 @@
<?= $this->extend('layouts/main') ?> <?= $this->extend('layouts/main') ?>
<?= $this->section('content') ?> <?= $this->section('content') ?>
<style>
th {
font-size: 11px;
text-align: center;
}
td {
text-align: center;
}
#resultList tbody tr {
cursor: pointer;
font-size: 12px;
}
.blockUI {
z-index: 1500 !important;
}
.ellipsis {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 180px;
}
.card-header {
display: flex !important;
align-items: center;
}
.card-header-tab {
justify-content: flex-start !important;
}
.table-scroll {
max-height: 300px;
overflow-y: scroll;
}
.swal2-cancel {
background-color: #ff0000 !important;
color: #fff !important;
}
</style>
<h1>전화확인매물 현황</h1>
<div class="col-md-12 col-xl-12"> <div class="col-md-12 col-xl-12">
<div class="main-card mb-3 card"> <div class="main-card mb-3 card">
<div class="card-header bg-white border-bottom shadow-sm">
<div class="d-flex flex-wrap align-items-center gap-3 card-header-tab">
<div>
<h4 class="mb-0 fw-bold text-dark">전화확인매물 현황</h4>
</div>
</div>
</div>
<div class="card-body"> <div class="card-body">
<form id="frm_srch_info" method="get" onsubmit="return false;"> <form id="frm_srch_info" method="get" onsubmit="return false;">
<input type="hidden" name="m" id="m" value="M801" /> <input type="hidden" name="m" id="m" value="M801" />
<input type="hidden" name="todo" id="todo" value="inq" /> <input type="hidden" name="todo" id="todo" value="inq" />
<input type="hidden" name="usr_id" value="" /> <input type="hidden" name="usr_id" value="" />
<!-- 안내 -->
<div class="alert alert-warning py-2 mb-3">
<small class="mb-0">
매물번호를 입력하면 <b>다른 조건은 무시</b>됩니다.
</small>
</div>
<!-- 검색 폼 --> <!-- 검색 폼 -->
<div class="row g-3"> <div class="row g-3">
<!-- 매물번호 --> <!-- 매물번호 -->
<div class="col-md-1"> <div class="col-md-1">
<label class="form-label mb-1">매물번호</label> <label class="form-label mb-1">
매물번호
<i class="pe-7s-info info-tooltip" data-bs-toggle="tooltip" data-bs-placement="top"
title="매물번호를 입력하면 다른 조건은 무시됩니다"></i>
</label>
<input type="text" name="atcl_no" class="form-control form-control-sm" placeholder="매물번호" maxlength="10" <input type="text" name="atcl_no" class="form-control form-control-sm" placeholder="매물번호" maxlength="10"
data-bs-toggle="tooltip" data-bs-placement="top" title="매물번호를 입력하면 다른 조건은 무시됩니다"
onkeypress="atcl_no_enter(event)"> onkeypress="atcl_no_enter(event)">
</div> </div>
@@ -79,10 +38,8 @@
<label class="form-label mb-1">현재상태</label> <label class="form-label mb-1">현재상태</label>
<select name="stat_cd" class="form-select form-select-sm"> <select name="stat_cd" class="form-select form-select-sm">
<option value="">-선택-</option> <option value="">-선택-</option>
<?php foreach ($codes as $c): ?> <?php foreach (($codes['STEP_VERIFICATION']['items'] ?? []) as $cd => $cdNm): ?>
<?php if ($c['category'] === "STEP_VERIFICATION"): ?> <option value="<?= $cd ?>"><?= $cdNm ?></option>
<option value="<?= $c['cd'] ?>"><?= $c['cd_nm'] ?></option>
<?php endif; ?>
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
</div> </div>
@@ -126,12 +83,10 @@
<div class="input-group input-group-sm"> <div class="input-group input-group-sm">
<select name="stat_complete_date" class="form-select form-select-sm"> <select name="stat_complete_date" class="form-select form-select-sm">
<option value="">-선택-</option> <option value="">-선택-</option>
<?php foreach ($codes as $c): ?> <?php foreach (($codes['STEP_VERIFICATION']['items'] ?? []) as $cd => $cdNm): ?>
<?php if ($c['category'] === "STEP_VERIFICATION"): ?> <option value="<?= $cd ?>">
<option value="<?= $c['cd'] ?>"> <?= $cdNm ?>
<?= $c['cd_nm'] ?>
</option> </option>
<?php endif; ?>
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
<input type="date" class="form-control" name="complete_sdate" id="complete_sdate" placeholder="시작일"> <input type="date" class="form-control" name="complete_sdate" id="complete_sdate" placeholder="시작일">
@@ -186,10 +141,8 @@
<label class="form-label mb-1">매체사</label> <label class="form-label mb-1">매체사</label>
<select name="rcpt_cpid" class="form-select form-select-sm"> <select name="rcpt_cpid" class="form-select form-select-sm">
<option value="">-전체-</option> <option value="">-전체-</option>
<?php foreach ($codes as $c): ?> <?php foreach (($codes['CP_ID']['items'] ?? []) as $cd => $cdNm): ?>
<?php if ($c['category'] === "CP_ID"): ?> <option value="<?= $cd ?>"><?= $cdNm ?></option>
<option value="<?= $c['cd'] ?>"><?= $c['cd_nm'] ?></option>
<?php endif; ?>
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
</div> </div>
@@ -199,10 +152,8 @@
<label class="form-label mb-1">매물종류</label> <label class="form-label mb-1">매물종류</label>
<select name="rlet_type_cd" class="form-select form-select-sm"> <select name="rlet_type_cd" class="form-select form-select-sm">
<option value="">-매물종류-</option> <option value="">-매물종류-</option>
<?php foreach ($codes as $c): ?> <?php foreach (($codes['ARTICLE_TYPE']['items'] ?? []) as $cd => $cdNm): ?>
<?php if ($c['category'] === "ARTICLE_TYPE"): ?> <option value="<?= $cd ?>"><?= $cdNm ?></option>
<option value="<?= $c['cd'] ?>"><?= $c['cd_nm'] ?></option>
<?php endif; ?>
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
</div> </div>
@@ -228,14 +179,6 @@
</div> </div>
<div class="ml-auto">
<button class="btn btn-sm btn-outline-success" id="excel-download">
<i class="fa fa-fw" aria-hidden="true" title="file-excel-o"></i>
엑셀다운로드
</button>
</div>
</div> </div>
<div class="card-body"> <div class="card-body">
@@ -265,11 +208,19 @@
</div> </div>
</div> </div>
<?= $this->endSection() ?>
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.13.6/css/jquery.dataTables.min.css" /> <?= $this->section('page_styles') ?>
<?= $this->include('layouts/partials/datatables_bs5_css') ?>
<link href="https://unpkg.com/dropzone@6.0.0-beta.1/dist/dropzone.css" rel="stylesheet" type="text/css" /> <link href="https://unpkg.com/dropzone@6.0.0-beta.1/dist/dropzone.css" rel="stylesheet" type="text/css" />
<script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script> <style>
<script defer src="/architectui/assets/js/datatable.kor.js"></script> #resultList_wrapper .dt-start { display: flex; align-items: center; gap: 1rem; align-items: baseline;}
</style>
<?= $this->endSection() ?>
<?= $this->section('page_scripts') ?>
<?= $this->include('layouts/partials/datatables_bs5_js') ?>
<?= $this->include('layouts/partials/datatables_v2_layout_helpers') ?>
<script type="text/javascript" src="https://oapi.map.naver.com/openapi/v3/maps.js?ncpKeyId=dtounkwjc5"></script> <script type="text/javascript" src="https://oapi.map.naver.com/openapi/v3/maps.js?ncpKeyId=dtounkwjc5"></script>
<script type="text/javascript"> <script type="text/javascript">
@@ -277,11 +228,21 @@
const bonbuArr = <?= json_encode($bonbu, JSON_UNESCAPED_UNICODE); ?>; const bonbuArr = <?= json_encode($bonbu, JSON_UNESCAPED_UNICODE); ?>;
const teamArr = <?= json_encode($team, JSON_UNESCAPED_UNICODE); ?>; const teamArr = <?= json_encode($team, JSON_UNESCAPED_UNICODE); ?>;
const userArr = <?= json_encode($user, JSON_UNESCAPED_UNICODE); ?>; const userArr = <?= json_encode($user, JSON_UNESCAPED_UNICODE); ?>;
const codes = <?= json_encode($codes, JSON_UNESCAPED_UNICODE); ?>;
var table; var table;
$(function () { $(function () {
// Bootstrap tooltip 초기화
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl)
});
// localStorage에서 검색 조건 복원
restoreFormData();
$("#bonbu").on("change", function (e) { $("#bonbu").on("change", function (e) {
const value = e.target.value const value = e.target.value
@@ -454,6 +415,10 @@
initReceiptDate(); initReceiptDate();
table = $('#resultList').DataTable({ table = $('#resultList').DataTable({
layout: {
topEnd: v2TopEndButtons(),
...v2BottomLayout()
},
language: lang_kor, language: lang_kor,
serverSide: true, serverSide: true,
processing: true, processing: true,
@@ -511,7 +476,7 @@
{ data: null, render: fn_addr_render }, { data: null, render: fn_addr_render },
{ data: 'cpid' }, { data: 'cpid' },
{ data: 'realtor_nm' }, { data: 'realtor_nm' },
{ data: null, width: "80px", fn_render_nm }, { data: null, width: "80px", render: fn_render_nm },
{ data: null, render: fn_tm_render }, { data: null, render: fn_tm_render },
{ data: 'reg_charger' }, { data: 'reg_charger' },
{ data: 'result_tm' }, { data: 'result_tm' },
@@ -534,6 +499,7 @@
}); });
$('#btnSearch').on('click', function () { $('#btnSearch').on('click', function () {
saveFormData(); // 검색 조건 저장
table.ajax.reload() table.ajax.reload()
}); });
@@ -589,14 +555,6 @@
return str; return str;
} }
function fn_region_render(data, type, row) {
var str = "";
str = row.region_nm + " " + row.rm_no;
return str;
}
// 주소 render // 주소 render
function fn_addr_render(data, type, row) { function fn_addr_render(data, type, row) {
var str = ""; var str = "";
@@ -712,5 +670,29 @@
const modal = new bootstrap.Modal(document.getElementById('previewModal')); const modal = new bootstrap.Modal(document.getElementById('previewModal'));
modal.show(); modal.show();
} }
// localStorage에 검색 조건 저장
function saveFormData() {
const formData = $('#frm_srch_info').serializeArray();
const formObject = {};
formData.forEach(item => {
formObject[item.name] = item.value;
});
localStorage.setItem('m704_search_form', JSON.stringify(formObject));
}
// localStorage에서 검색 조건 복원
function restoreFormData() {
const savedData = localStorage.getItem('m704_search_form');
if (savedData) {
const formObject = JSON.parse(savedData);
Object.keys(formObject).forEach(key => {
const $element = $('#frm_srch_info').find(`[name="${key}"]`);
if ($element.length) {
$element.val(formObject[key]);
}
});
}
}
</script> </script>
<?= $this->endSection() ?> <?= $this->endSection() ?>

Some files were not shown because too many files have changed in this diff Show More