This commit is contained in:
2026-01-30 17:20:52 +09:00
commit 21b6332c9c
459 changed files with 190743 additions and 0 deletions

1
kngil/bbs/.htaccess Normal file
View File

@@ -0,0 +1 @@
Options -Indexes

165
kngil/bbs/adm.php Normal file
View File

@@ -0,0 +1,165 @@
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
header('Content-Type: application/json; charset=utf-8');
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
require_once __DIR__ . '/db_conn.php';
/* =================================================
0. 로그인 / 권한 체크
================================================= */
if (!isset($_SESSION['login'])) {
echo json_encode([
'status' => 'error',
'message' => '로그인이 필요합니다.'
]);
exit;
}
$auth = $_SESSION['login']['auth_bc'] ?? '';
if (!in_array($auth, ['BS100100', 'BS100200'])) {
echo json_encode([
'status' => 'error',
'message' => '접근 권한이 없습니다.'
]);
exit;
}
/* =================================================
공통 입력
================================================= */
$input = json_decode(file_get_contents('php://input'), true) ?? [];
$action = $_GET['action'] ?? $input['action'] ?? 'list';
try {
switch ($action) {
case 'user_list':
// 🔥 상단에서 선택한 회사
$target_member_id = $_GET['member_id'] ?? '';
if (!$target_member_id) {
throw new Exception('member_id 누락');
}
$stmt = $pdo->prepare("
SELECT *
FROM kngil.sp_users_r(
:member_id,
'',
'',
''
)
");
$stmt->execute([
':member_id' => $target_member_id
]);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($rows as $i => &$r) {
$r['recid'] = $i + 1;
}
echo json_encode([
'status' => 'success',
'records' => $rows
]);
break;
/* =================================================
1. 통합 회원 목록 조회
- 모든 회사(member) 조회
================================================= */
case 'list':
$sql = "SELECT * FROM kngil.sp_member_sys_r('', '', '');";
$rows = $pdo->query($sql)->fetchAll(PDO::FETCH_ASSOC);
$i = 1;
foreach ($rows as &$r) {
$r['recid'] = $i++;
}
echo json_encode([
'status' => 'success',
'records' => $rows
]);
break;
/* =================================================
2. 회원 정보 수정 (회사 단위)
- tel / email / 사업자번호 / 회사명
================================================= */
case 'save':
$updates = $input['updates'] ?? [];
if (!$updates) {
throw new Exception('저장할 데이터가 없습니다.');
}
$pdo->beginTransaction();
$stmtU = $pdo->prepare("
SELECT kngil.sp_member_sys_u(
:member_id::varchar,
:tel_no::varchar,
:email::varchar,
:bs_no::varchar,
:co_nm::varchar,
:mid::varchar
) AS result
");
foreach ($updates as $r) {
if (empty($r['member_id'])) {
throw new Exception('member_id 누락');
}
$stmtU->execute([
':member_id' => $r['member_id'],
':tel_no' => $r['tel_no'] ?? null,
':email' => $r['email'] ?? null,
':bs_no' => $r['bs_no'] ?? null,
':co_nm' => $r['co_nm'] ?? null,
':mid' => $_SESSION['login']['user_id']
]);
$result = $stmtU->fetchColumn();
if (strpos($result, 'ERROR') === 0) {
throw new Exception($result);
}
}
$pdo->commit();
echo json_encode([
'status' => 'success'
]);
break;
default:
throw new Exception('Invalid action');
}
} catch (Exception $e) {
if ($pdo->inTransaction()) {
$pdo->rollBack();
}
echo json_encode([
'status' => 'error',
'message' => $e->getMessage()
]);
}

261
kngil/bbs/adm_comp copy.php Normal file
View File

@@ -0,0 +1,261 @@
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
header('Content-Type: application/json; charset=utf-8');
/* =========================
로그인 / 권한 가드
========================= */
require_once __DIR__ . '/adm_guard.php';
require_once __DIR__ . '/db_conn.php';
/* =========================
세션 기준 값 (중요)
========================= */
$login = $_SESSION['login'];
$auth_bc = $login['auth_bc']; // 권한코드
$member_id = $login['member_id']; // 회사ID
/* =========================
입력
========================= */
$input = json_decode(file_get_contents('php://input'), true) ?? [];
$action = $_GET['action'] ?? $input['action'] ?? 'list';
/* =========================
권한 플래그
========================= */
$isAdmin = in_array($auth_bc, ['BS100100','BS100200','BS100300'], true);
try {
switch ($action) {
/* =========================
0. 공통코드 조회 (콤보)
========================= */
case 'base_code':
$main_cd = $_GET['main_cd'] ?? $input['main_cd'] ?? '';
if (!$main_cd) {
throw new Exception('main_cd가 필요합니다.');
}
$stmt = $pdo->prepare("
SELECT *
FROM kngil.fn_base_cd(:main_cd)
");
$stmt->execute([
':main_cd' => $main_cd
]);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo json_encode([
'status' => 'success',
'items' => $rows // [{id, text}]
]);
break;
/* =========================
1. 사용자 목록 조회
========================= */
case 'list':
$schType = $_GET['sch_type'] ?? '';
$schKeyword = $_GET['sch_keyword'] ?? '';
$schUseYn = $_GET['sch_use_yn'] ?? '';
// 기본값
$sch_id = '';
$sch_nm = '';
$sch_dept = '';
if ($schKeyword !== '') {
switch ($schType) {
case 'id':
$sch_id = $schKeyword;
break;
case 'name':
$sch_nm = $schKeyword;
break;
case 'dept':
$sch_dept = $schKeyword;
break;
default: // 전체
$sch_id = $schKeyword;
$sch_nm = $schKeyword;
$sch_dept = $schKeyword;
}
}
$sql = "
SELECT *
FROM kngil.sp_users_r(
:member_id,
:user_nm,
:dept_nm,
:use_yn
);
";
$stmt = $pdo->prepare($sql);
$stmt->execute([
':member_id' => $member_id,
':user_nm' => $_GET['user_nm'] ?? '',
':dept_nm' => $_GET['dept_nm'] ?? '',
':use_yn' => $_GET['use_yn'] ?? ''
]);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
$i = 1;
foreach ($rows as &$r) {
$r['recid'] = $i++;
}
echo json_encode([
'status' => 'success',
'member_id' => $member_id,
'records' => $rows
]);
break;
/* =========================
2. 사용자 저장
========================= */
case 'save':
$inserts = $input['inserts'] ?? [];
$updates = $input['updates'] ?? [];
if (!$inserts && !$updates) {
throw new Exception('저장할 데이터가 없습니다.');
}
$pdo->beginTransaction();
// INSERT
if ($inserts) {
$stmtI = $pdo->prepare("
SELECT kngil.sp_users_i(
:member_id,:user_id,:user_pw,:user_nm,:dept_nm,
:posit_nm,:tel_no,:email,:auth_bc,:use_yn,:rmks,:cid
)
");
foreach ($inserts as $r) {
$stmtI->execute([
':member_id' => $member_id,
':user_id' => $r['user_id'],
':user_pw' => $r['user_pw'] ?? '0000',
':user_nm' => $r['user_nm'],
':dept_nm' => $r['dept_nm'],
':posit_nm' => $r['posit_nm'] ?? '',
':tel_no' => $r['tel_no'],
':email' => $r['email'],
':auth_bc' => $r['auth_bc'],
':use_yn' => $r['use_yn'],
':rmks' => $r['rmks'] ?? '',
':cid' => $r['cid'] ?? 'SYSTEM'
]);
}
}
// UPDATE
if ($updates) {
$stmtU = $pdo->prepare("
SELECT kngil.sp_users_u(
:member_id,:user_id,:user_pw,:user_nm,:dept_nm,
:posit_nm,:tel_no,:email,:auth_bc,:use_yn,:rmks,:mid
)
");
foreach ($updates as $r) {
$stmtU->execute([
':member_id' => $member_id,
':user_id' => $r['user_id'],
':user_pw' => null,
':user_nm' => $r['user_nm'],
':dept_nm' => $r['dept_nm'],
':posit_nm' => $r['posit_nm'] ?? '',
':tel_no' => $r['tel_no'],
':email' => $r['email'],
':auth_bc' => $r['auth_bc'],
':use_yn' => $r['use_yn'],
':rmks' => $r['rmks'] ?? '',
':mid' => $r['mid'] ?? 'SYSTEM'
]);
}
}
$pdo->commit();
echo json_encode(['status'=>'success']);
break;
/* =========================
3. 사용자 삭제 (비활성)
========================= */
case 'delete':
$ids = $input['ids'] ?? [];
if (!$ids) throw new Exception('삭제 대상이 없습니다.');
$sql = "SELECT kngil.sp_users_d(:member_id, :user_id)";
$stmt = $pdo->prepare($sql);
foreach ($ids as $uid) {
$stmt->execute([
':member_id' => $member_id,
':user_id' => $uid
]);
}
echo json_encode(['status'=>'success']);
break;
/* =========================
4. 회원 총 구매 면적 조회
========================= */
case 'total_area':
$sql = "
SELECT COALESCE(SUM(sum_area), 0) AS total_area
FROM kngil.sp_buy_item_history_r(:member_id, '', NULL, NULL)
";
$stmt = $pdo->prepare($sql);
$stmt->execute([
':member_id' => $member_id
]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
echo json_encode([
'status' => 'success',
'member_id' => $member_id,
'total_area' => (int)$row['total_area']
]);
break;
default:
throw new Exception('잘못된 요청');
}
} catch (Exception $e) {
if ($pdo->inTransaction()) {
$pdo->rollBack();
}
http_response_code(500);
echo json_encode([
'status' => 'error',
'message' => $e->getMessage()
]);
}

393
kngil/bbs/adm_comp.php Normal file
View File

@@ -0,0 +1,393 @@
<?php
ini_set('display_errors', 0);
error_reporting(0);
header('Content-Type: application/json; charset=utf-8');
/* =========================
로그인 / 권한 가드
========================= */
require_once __DIR__ . '/adm_guard.php';
require_once __DIR__ . '/db_conn.php';
/* =========================
세션 기준 값
========================= */
$login = $_SESSION['login'];
$auth_bc = $login['auth_bc']; // 권한코드
$member_id = $login['member_id']; // 회사ID
$user_id = $login['user_id'] ?? 'SYSTEM';
/* =========================
권한 정의 (단순화 버전)
========================= */
$isAdmin = in_array(
$auth_bc,
['BS100100','BS100200','BS100300','BS100400'],
true
);
/* =========================
입력
========================= */
$input = json_decode(file_get_contents('php://input'), true) ?? [];
$action = $_GET['action'] ?? $input['action'] ?? 'list';
try {
switch ($action) {
/* =========================
0. 공통코드 조회
========================= */
case 'base_code':
$main_cd = $_GET['main_cd'] ?? $input['main_cd'] ?? '';
if (!$main_cd) {
throw new Exception('main_cd가 필요합니다.');
}
$stmt = $pdo->prepare("
SELECT *
FROM kngil.fn_base_cd(:main_cd)
");
$stmt->execute([':main_cd' => $main_cd]);
echo json_encode([
'status' => 'success',
'items' => $stmt->fetchAll(PDO::FETCH_ASSOC)
]);
break;
/* =========================
1. 사용자 목록 조회
========================= */
case 'list':
// 조회 대상 member_id 결정
$target_member_id = $member_id; // 기본: 세션 기준
// 관리자 / 개발자만 GET member_id 허용
if ($isAdmin && !empty($_GET['member_id'])) {
$target_member_id = $_GET['member_id'];
}
$stmt = $pdo->prepare("
SELECT *
FROM kngil.sp_users_r(
:member_id,
:user_nm,
:dept_nm,
:use_yn
)
");
$stmt->execute([
':member_id' => $target_member_id,
':user_nm' => $_GET['user_nm'] ?? '',
':dept_nm' => $_GET['dept_nm'] ?? '',
':use_yn' => $_GET['use_yn'] ?? ''
]);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($rows as $i => &$r) {
$r['recid'] = $i + 1;
}
echo json_encode([
'status' => 'success',
'member_id' => $target_member_id,
'records' => $rows
]);
break;
/* =========================
2. 사용자 저장
========================= */
case 'save':
if (!$isAdmin) {
throw new Exception('저장 권한이 없습니다.');
}
$inserts = $input['inserts'] ?? [];
$updates = $input['updates'] ?? [];
if (!$inserts && !$updates) {
throw new Exception('저장할 데이터가 없습니다.');
}
$pdo->beginTransaction();
/* ---------- INSERT ---------- */
if ($inserts) {
$stmtI = $pdo->prepare("
SELECT kngil.sp_users_i(
:member_id,:user_id,:user_pw,:user_nm,:dept_nm,
:posit_nm,:tel_no,:email,:auth_bc,:use_yn,:rmks,:cid
)
");
foreach ($inserts as $r) {
$stmtI->execute([
':member_id' => $member_id,
':user_id' => $r['user_id'],
':user_pw' => $r['user_pw'] ?? '0000',
':user_nm' => $r['user_nm'],
':dept_nm' => $r['dept_nm'],
':posit_nm' => $r['posit_nm'] ?? '',
':tel_no' => $r['tel_no'],
':email' => $r['email'],
':auth_bc' => is_array($r['auth_bc'])
? ($r['auth_bc']['id'] ?? '')
: $r['auth_bc'],
':use_yn' => is_array($r['use_yn'])
? ($r['use_yn']['id'] ?? 'Y')
: $r['use_yn'],
':rmks' => $r['rmks'] ?? '',
':cid' => $user_id
]);
}
}
/* ---------- UPDATE ---------- */
if ($updates) {
$stmtChk = $pdo->prepare("
SELECT 1
FROM kngil.users
WHERE member_id = :member_id
AND user_id = :user_id
");
$stmtU = $pdo->prepare("
SELECT kngil.sp_users_u(
:member_id,:user_id,NULL,:user_nm,:dept_nm,
:posit_nm,:tel_no,:email,:auth_bc,:use_yn,:rmks,:mid
)
");
foreach ($updates as $r) {
// 회사 탈출 방지
$stmtChk->execute([
':member_id' => $member_id,
':user_id' => $r['user_id']
]);
if (!$stmtChk->fetchColumn()) {
throw new Exception('권한 없는 사용자 수정 시도');
}
$stmtU->execute([
':member_id' => $member_id,
':user_id' => $r['user_id'],
':user_nm' => $r['user_nm'],
':dept_nm' => $r['dept_nm'],
':posit_nm' => $r['posit_nm'] ?? '',
':tel_no' => $r['tel_no'],
':email' => $r['email'],
':auth_bc' => $r['auth_bc'],
':use_yn' => $r['use_yn'],
':rmks' => $r['rmks'] ?? '',
':mid' => $user_id
]);
}
}
$pdo->commit();
echo json_encode(['status' => 'success']);
break;
/* =========================
3. 사용자 삭제 (비활성)
========================= */
case 'delete':
if (!$isAdmin) {
throw new Exception('삭제 권한이 없습니다.');
}
$ids = $input['ids'] ?? [];
if (!$ids) {
throw new Exception('삭제 대상이 없습니다.');
}
$stmt = $pdo->prepare("
SELECT kngil.sp_users_d(:member_id, :user_id)
");
foreach ($ids as $uid) {
$stmt->execute([
':member_id' => $member_id,
':user_id' => $uid
]);
}
echo json_encode(['status' => 'success']);
break;
/* =========================
4. 회원 총 구매 면적
========================= */
case 'total_area':
$stmt = $pdo->prepare("
SELECT COALESCE(SUM(sum_area),0)
FROM kngil.sp_buy_item_history_r(:member_id, '', NULL, NULL)
");
$stmt->execute([':member_id' => $member_id]);
echo json_encode([
'status' => 'success',
'total_area' => (int)$stmt->fetchColumn()
]);
break;
/* =========================
5. CSV 일괄 계정생성
========================= */
case 'bulk_create':
if (!$isAdmin) {
throw new Exception('일괄 생성 권한이 없습니다.');
}
$target_member_id = $input['member_id'] ?? '';
$csv_url = $input['csv_url'] ?? '';
if (!$target_member_id || !$csv_url) {
throw new Exception('필수 파라미터 누락');
}
// CSV 다운로드
$csv = @file_get_contents($csv_url);
if ($csv === false) {
throw new Exception('CSV 파일을 불러올 수 없습니다.');
}
$lines = array_map('str_getcsv', explode("\n", trim($csv)));
// 헤더 제거 (첫 줄)
array_shift($lines);
$success = 0;
$fail = 0;
$errors = [];
$stmt = $pdo->prepare("
SELECT kngil.sp_users_i(
:member_id,:user_id,:user_pw,:user_nm,:dept_nm,
'',:tel_no,:email,:auth_bc,:use_yn,'',
:cid
)
");
foreach ($lines as $i => $row) {
if (count($row) < 8) {
$fail++;
$errors[] = "라인 ".($i+2).": 컬럼 수 부족";
continue;
}
[
$user_id,
$user_pw,
$user_nm,
$tel_no,
$email,
$dept_nm,
$use_txt,
$auth_txt
] = array_map('trim', $row);
/* ---------- 값 정규화 ---------- */
$use_yn = 'Y'; // 항상 사용
$auth_bc = 'BS100500'; // 일반 권한
/* ---------- 중복 체크 ---------- */
// ID 중복 (대소문자 무시)
$chk = $pdo->prepare("
SELECT 1 FROM kngil.users
WHERE LOWER(user_id) = LOWER(:uid)
");
$chk->execute([':uid' => $user_id]);
if ($chk->fetchColumn()) {
$fail++;
$errors[] = "라인 ".($i+2).": ID 중복 ($user_id)";
continue;
}
// 전화번호 중복
$chk = $pdo->prepare("
SELECT 1 FROM kngil.users
WHERE tel_no = :tel
");
$chk->execute([':tel' => $tel_no]);
if ($chk->fetchColumn()) {
$fail++;
$errors[] = "라인 ".($i+2).": 연락처 중복 ($tel_no)";
continue;
}
/* ---------- 프로시저 호출 ---------- */
try {
$stmt->execute([
':member_id' => $target_member_id,
':user_id' => $user_id,
':user_pw' => $user_pw ?: '0000',
':user_nm' => $user_nm,
':dept_nm' => $dept_nm,
':tel_no' => $tel_no,
':email' => $email,
':auth_bc' => $auth_bc,
':use_yn' => $use_yn,
':cid' => $user_id // 생성자
]);
$result = $stmt->fetchColumn(); // ⭐ 필수
if ($result !== 'SUCCESS') {
$fail++;
$errors[] = "라인 ".($i+2).": ".$result;
continue;
}
$success++;
} catch (Exception $e) {
$fail++;
$errors[] = "라인 ".($i+2).": ".$e->getMessage();
}
}
echo json_encode([
'status' => 'success',
'success_cnt' => $success,
'fail_cnt' => $fail,
'errors' => $errors
]);
break;
default:
throw new Exception('잘못된 요청');
}
} catch (Exception $e) {
if ($pdo->inTransaction()) {
$pdo->rollBack();
}
http_response_code(403);
echo json_encode([
'status' => 'error',
'message' => $e->getMessage()
]);
}

View File

@@ -0,0 +1,27 @@
<?php
// 1. DB 연결 (절대 경로 혹은 상대 경로 확인)
require_once 'db_conn.php';
header('Content-Type: application/json');
try {
// 2. 프로시저 호출 (컬럼명: itm_cd, itm_nm, area, itm_amt 등)
$stmt = $pdo->prepare("SELECT * FROM kngil.sp_fa_comments_r()");
$stmt->execute();
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
// 3. W2UI를 위해 각 행에 'recid' 필드 강제 주입
$records = [];
foreach ($rows as $row) {
$row['recid'] = $row['fa_id']; // 상품코드를 고유 키로 지정
$records[] = $row;
}
// 4. 순수 JSON만 출력 (다른 echo나 공백이 섞이면 에러 발생)
echo json_encode($records);
} catch (PDOException $e) {
// 에러 발생 시 그리드가 이해할 수 있게 JSON으로 출력
echo json_encode(['status' => 'error', 'message' => $e->getMessage()]);
}
?>

View File

@@ -0,0 +1,58 @@
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
header('Content-Type: application/json; charset=utf-8');
/* =========================
로그인 / 권한 가드
========================= */
require_once __DIR__ . '/adm_guard.php';
require_once __DIR__ . '/db_conn.php';
/* =========================
세션 기준 값
========================= */
$login = $_SESSION['login'];
$auth_bc = $login['auth_bc']; // 권한코드
$member_id = $login['member_id']; // 회사ID
$user_id = $login['user_id'] ?? 'SYSTEM';
error_reporting(E_ALL);
ini_set('display_errors', 1);
header('Content-Type: application/json; charset=utf-8');
// JSON 데이터 읽기
$input = json_decode(file_get_contents('php://input'), true);
try {
// 2. $pdo 변수가 있는지 확인 (DB 연결 확인)
if (!isset($pdo)) {
throw new Exception('데이터베이스 연결 설정($pdo)을 찾을 수 없습니다.');
}
$ids = $input['ids'] ?? [];
if (empty($ids)) {
throw new Exception('삭제 대상이 없습니다.');
}
$pdo->beginTransaction();
// 3. 삭제 실행
$stmt = $pdo->prepare("SELECT kngil.sp_fa_comments_d(:fa_id)");
foreach ($ids as $code) {
$stmt->execute([':fa_id' => $code]);
}
$pdo->commit();
echo json_encode(['status' => 'success']);
} catch (Exception $e) {
if (isset($pdo) && $pdo->inTransaction()) {
$pdo->rollBack();
}
// 기존 출력물 제거 후 깨끗한 JSON만 출력
if (ob_get_length()) ob_clean();
echo json_encode(['status' => 'error', 'message' => $e->getMessage()]);
}
?>

View File

@@ -0,0 +1,94 @@
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
header('Content-Type: application/json; charset=utf-8');
/* =========================
로그인 / 권한 가드
========================= */
require_once __DIR__ . '/adm_guard.php';
require_once __DIR__ . '/db_conn.php';
/* =========================
세션 기준 값
========================= */
$login = $_SESSION['login'];
$auth_bc = $login['auth_bc']; // 권한코드
$member_id = $login['member_id']; // 회사ID
$user_id = $login['user_id'] ?? 'SYSTEM';
/* =========================
입력
========================= */
$input = json_decode(file_get_contents('php://input'), true) ?? [];
$action = $_GET['action'] ?? $input['action'] ?? 'list';
try {
switch ($action) {
case 'save':
// $isAdmin 에러를 방지하기 위해 잠시 주석 처리하거나 true로 설정
// if (!$isAdmin) throw new Exception('권한이 없습니다.');
$inserts = $input['inserts'] ?? [];
$updates = $input['updates'] ?? [];
$pdo->beginTransaction();
/* ---------- 신규 추가(INSERT) ---------- */
if ($inserts) {
// 호출 시 파라미터 개수와 이름을 정확히 맞춤
$stmtI = $pdo->prepare("SELECT kngil.sp_fa_comments_i(:fa_subject, :fa_content, :sq_no, :use_yn, :cid)");
foreach ($inserts as $r) {
$stmtI->execute([
':fa_subject' => $r['fa_subject'] ?? '', // 데이터가 없으면 빈 글자라도 보냄
':fa_content' => $r['fa_content'] ?? '',
':sq_no' => $r['sq_no'] ?? 0,
':use_yn' => $r['use_yn'] ?? 'Y',
':cid' => $user_id
]);
}
}
/* ---------- 수정(UPDATE) ---------- */
if ($updates) {
$stmtU = $pdo->prepare("SELECT kngil.sp_fa_comments_u(:fa_id, :fa_subject, :fa_content, :sq_no, :use_yn, :mid)");
foreach ($updates as $r) {
$stmtU->execute([
':fa_id' => $r['fa_id'] ?? '',
':fa_subject' => $r['fa_subject'] ?? '', // 데이터가 없으면 빈 글자라도 보냄
':fa_content' => $r['fa_content'] ?? '',
':sq_no' => $r['sq_no'] ?? 0,
':use_yn' => $r['use_yn'] ?? 'Y',
':mid' => $user_id
]);
}
}
$pdo->commit();
echo json_encode(['status' => 'success']);
break;
default:
throw new Exception('잘못된 요청');
}
}
catch (Exception $e) {
if ($pdo->inTransaction()) {
$pdo->rollBack();
}
http_response_code(403);
echo json_encode([
'status' => 'error',
'message' => $e->getMessage()
]);
}

36
kngil/bbs/adm_guard.php Normal file
View File

@@ -0,0 +1,36 @@
<?php
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
/* =========================
1. 로그인 체크
========================= */
if (empty($_SESSION['login'])) {
header('Location: /kngil/skin/index.php');
exit;
}
/* =========================
2. 권한 체크
========================= */
$auth_bc = $_SESSION['login']['auth_bc'] ?? '';
$ALLOW_AUTH = [
'BS100100', // 개발자
'BS100200', // 관리자
'BS100300', // 메인
'BS100400', // 서브
];
if (!in_array($auth_bc, $ALLOW_AUTH, true)) {
http_response_code(403);
echo '접근 권한이 없습니다.';
exit;
}
/* =========================
3. 권한 플래그 (중요)
========================= */
define('IS_SUPER_ADMIN', in_array($auth_bc, ['BS100100','BS100200'], true));
define('IS_COMPANY_ADMIN', in_array($auth_bc, ['BS100300','BS100400'], true));

View File

@@ -0,0 +1,27 @@
<?php
// 1. DB 연결 (절대 경로 혹은 상대 경로 확인)
require_once 'db_conn.php';
header('Content-Type: application/json');
try {
// 2. 프로시저 호출 (컬럼명: itm_cd, itm_nm, area, itm_amt 등)
$stmt = $pdo->prepare("SELECT * FROM kngil.sp_item_r()");
$stmt->execute();
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
// 3. W2UI를 위해 각 행에 'recid' 필드 강제 주입
$records = [];
foreach ($rows as $row) {
$row['recid'] = $row['itm_cd']; // 상품코드를 고유 키로 지정
$records[] = $row;
}
// 4. 순수 JSON만 출력 (다른 echo나 공백이 섞이면 에러 발생)
echo json_encode($records);
} catch (PDOException $e) {
// 에러 발생 시 그리드가 이해할 수 있게 JSON으로 출력
echo json_encode(['status' => 'error', 'message' => $e->getMessage()]);
}
?>

View File

@@ -0,0 +1,58 @@
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
header('Content-Type: application/json; charset=utf-8');
/* =========================
로그인 / 권한 가드
========================= */
require_once __DIR__ . '/adm_guard.php';
require_once __DIR__ . '/db_conn.php';
/* =========================
세션 기준 값
========================= */
$login = $_SESSION['login'];
$auth_bc = $login['auth_bc']; // 권한코드
$member_id = $login['member_id']; // 회사ID
$user_id = $login['user_id'] ?? 'SYSTEM';
error_reporting(E_ALL);
ini_set('display_errors', 1);
header('Content-Type: application/json; charset=utf-8');
// JSON 데이터 읽기
$input = json_decode(file_get_contents('php://input'), true);
try {
// 2. $pdo 변수가 있는지 확인 (DB 연결 확인)
if (!isset($pdo)) {
throw new Exception('데이터베이스 연결 설정($pdo)을 찾을 수 없습니다.');
}
$ids = $input['ids'] ?? [];
if (empty($ids)) {
throw new Exception('삭제 대상이 없습니다.');
}
$pdo->beginTransaction();
// 3. 삭제 실행
$stmt = $pdo->prepare("SELECT kngil.sp_item_d(:itm_cd)");
foreach ($ids as $code) {
$stmt->execute([':itm_cd' => $code]);
}
$pdo->commit();
echo json_encode(['status' => 'success']);
} catch (Exception $e) {
if (isset($pdo) && $pdo->inTransaction()) {
$pdo->rollBack();
}
// 기존 출력물 제거 후 깨끗한 JSON만 출력
if (ob_get_length()) ob_clean();
echo json_encode(['status' => 'error', 'message' => $e->getMessage()]);
}
?>

View File

@@ -0,0 +1,97 @@
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
header('Content-Type: application/json; charset=utf-8');
/* =========================
로그인 / 권한 가드
========================= */
require_once __DIR__ . '/adm_guard.php';
require_once __DIR__ . '/db_conn.php';
/* =========================
세션 기준 값
========================= */
$login = $_SESSION['login'];
$auth_bc = $login['auth_bc']; // 권한코드
$member_id = $login['member_id']; // 회사ID
$user_id = $login['user_id'] ?? 'SYSTEM';
/* =========================
입력
========================= */
$input = json_decode(file_get_contents('php://input'), true) ?? [];
$action = $_GET['action'] ?? $input['action'] ?? 'list';
try {
switch ($action) {
case 'save':
// $isAdmin 에러를 방지하기 위해 잠시 주석 처리하거나 true로 설정
// if (!$isAdmin) throw new Exception('권한이 없습니다.');
$inserts = $input['inserts'] ?? [];
$updates = $input['updates'] ?? [];
$pdo->beginTransaction();
/* ---------- 신규 추가(INSERT) ---------- */
if ($inserts) {
// 호출 시 파라미터 개수와 이름을 정확히 맞춤
$stmtI = $pdo->prepare("SELECT kngil.sp_item_i(:itm_cd, :itm_nm, :area, :itm_amt, :use_yn, :rmks, :cid)");
foreach ($inserts as $r) {
$stmtI->execute([
':itm_cd' => $r['itm_cd'] ?? '', // 데이터가 없으면 빈 글자라도 보냄
':itm_nm' => $r['itm_nm'] ?? '',
':area' => $r['area'] ?? 0,
':itm_amt' => $r['itm_amt'] ?? 0,
':use_yn' => $r['use_yn'] ?? 'Y',
':rmks' => $r['rmks'] ?? '',
':cid' => $user_id
]);
}
}
/* ---------- 수정(UPDATE) ---------- */
if ($updates) {
$stmtU = $pdo->prepare("SELECT kngil.sp_item_u(:itm_cd, :itm_nm, :area, :itm_amt, :use_yn, :rmks, :mid)");
foreach ($updates as $r) {
$stmtU->execute([
':itm_cd' => $r['itm_cd'] ?? '',
':itm_nm' => $r['itm_nm'] ?? '',
':area' => $r['area'] ?? 0,
':itm_amt' => $r['itm_amt'] ?? 0,
':use_yn' => $r['use_yn'] ?? 'Y',
':rmks' => $r['rmks'] ?? '',
':mid' => $user_id
]);
}
}
$pdo->commit();
echo json_encode(['status' => 'success']);
break;
default:
throw new Exception('잘못된 요청');
}
}
catch (Exception $e) {
if ($pdo->inTransaction()) {
$pdo->rollBack();
}
http_response_code(403);
echo json_encode([
'status' => 'error',
'message' => $e->getMessage()
]);
}

View File

@@ -0,0 +1,45 @@
<?php
// 1. DB 연결
require_once 'db_conn.php';
header('Content-Type: application/json');
try {
// 클라이언트(JS)로부터 전달받은 검색 조건 (POST 또는 GET 방식에 맞춰 수정)
$p_member_id = $_POST['member_id'] ?? '';
$p_member_nm = $_POST['member_nm'] ?? '';
// 날짜가 빈 값('')으로 오면 반드시 null로 치환해야 PostgreSQL date 타입 에러가 안 납니다.
$p_fbuy_dt = (!empty($_POST['fbuy_dt'])) ? $_POST['fbuy_dt'] : null;
$p_tbuy_dt = (!empty($_POST['tbuy_dt'])) ? $_POST['tbuy_dt'] : null;
// 2. 프로시저 호출 - 파라미터 자리에 ?를 사용합니다.
$stmt = $pdo->prepare("SELECT * FROM kngil.sp_buy_item_history_r(?, ?, ?, ?)");
// 파라미터 순서대로 배열에 담아 실행합니다.
$stmt->execute([
$p_member_id,
$p_member_nm,
$p_fbuy_dt,
$p_tbuy_dt
]);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
// 3. W2UI를 위해 각 행에 'recid' 필드 강제 주입
$records = [];
foreach ($rows as $row) {
// 복합키(member_id + sp_no)를 조합하여 유일한 recid 생성
$row['recid'] = $row['member_id'] . '_' . $row['sq_no'];
$records[] = $row;
}
// 4. 결과 출력
echo json_encode($records);
} catch (PDOException $e) {
// HTTP 상태 코드를 500으로 설정하여 클라이언트가 에러임을 인지하게 할 수 있습니다.
http_response_code(500);
echo json_encode(['status' => 'error', 'message' => $e->getMessage()]);
}
?>

209
kngil/bbs/adm_service.php Normal file
View File

@@ -0,0 +1,209 @@
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
header('Content-Type: application/json; charset=utf-8');
require_once __DIR__ . '/db_conn.php';
$input = json_decode(file_get_contents('php://input'), true);
$action = $input['action'] ?? '';
$member_id = $input['member_id'] ?? '';
if (!$action || !$member_id) {
http_response_code(400);
echo json_encode([
'status' => 'error',
'message' => '필수값 누락'
]);
exit;
}
try {
/* =========================
조회 (R)
========================= */
if ($action === 'list') {
$buy_date = $input['buy_date'] ?? '';
if (!$buy_date) {
throw new Exception('구매일 누락');
}
$stmt = $pdo->prepare("
SELECT *
FROM kngil.sp_buy_item_r(:member_id, :buy_date)
");
$stmt->execute([
'member_id' => $member_id,
'buy_date' => $buy_date
]);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
$i = 1;
foreach ($rows as &$r) {
$r['recid'] = $i++;
}
echo json_encode([
'status' => 'success',
'records' => $rows
]);
exit;
}
/* =========================
즉시 삭제 (D)
========================= */
if ($action === 'delete') {
$sq_no = $input['sq_no'] ?? null;
if (!$sq_no) {
throw new Exception('삭제 대상 누락');
}
$pdo->prepare("
SELECT kngil.sp_buy_item_d(
:member_id,
:sq_no
)
")->execute([
'member_id' => $member_id,
'sq_no' => $sq_no
]);
echo json_encode([
'status' => 'success'
]);
exit;
}
/* =========================
저장 (C / U)
========================= */
if ($action === 'save') {
$buy_date = $input['buy_date'] ?? '';
$items = $input['items'] ?? [];
if (!$buy_date) {
throw new Exception('구매일 누락');
}
$pdo->beginTransaction();
foreach ($items as $row) {
$end_dt = empty($row['end_dt']) ? null : $row['end_dt'];
// INSERT
if (!empty($row['_new'])) {
$pdo->prepare("
SELECT kngil.sp_buy_item_i(
:member_id::character varying,
:buy_dt::date,
:itm_cd::character varying,
:itm_qty::numeric,
:itm_area::numeric,
:add_area::numeric,
(:itm_area + :add_area)::numeric,
:itm_amt::numeric,
:dis_rt::numeric,
:buy_amt::numeric,
:vat_amt::numeric,
:sum_amt::numeric,
:end_dt::date,
:ok_yn::character varying,
:rmks::character varying,
:cid::character varying
)
")->execute([
'member_id' => $member_id,
'buy_dt' => $buy_date,
'itm_cd' => $row['itm_cd'],
'itm_qty' => $row['itm_qty'],
'itm_area' => $row['itm_area'],
'add_area' => $row['add_area'] ?? 0,
'itm_amt' => $row['itm_amt'],
'dis_rt' => $row['dis_rt'],
'buy_amt' => $row['buy_amt'],
'vat_amt' => $row['vat_amt'],
'sum_amt' => $row['sum_amt'],
'end_dt' => $end_dt,
'ok_yn' => $row['ok_yn'],
'rmks' => $row['rmks'] ?? '',
'cid' => 'ADMIN'
]);
continue;
}
// UPDATE
if (!empty($row['_existing']) && !empty($row['sq_no'])) {
$pdo->prepare("
SELECT kngil.sp_buy_item_u(
:member_id::character varying,
:sq_no::integer,
:buy_dt::date,
:itm_cd::character varying,
:itm_qty::numeric,
:itm_area::numeric,
:add_area::numeric,
(:itm_area + :add_area)::numeric,
:itm_amt::numeric,
:dis_rt::numeric,
:buy_amt::numeric,
:vat_amt::numeric,
:sum_amt::numeric,
:end_dt::date,
:ok_yn::character varying,
:rmks::character varying,
:mid::character varying
)
")->execute([
'member_id' => $member_id,
'sq_no' => $row['sq_no'],
'buy_dt' => $buy_date,
'itm_cd' => $row['itm_cd'],
'itm_qty' => $row['itm_qty'],
'itm_area' => $row['itm_area'],
'add_area' => $row['add_area'] ?? 0,
'itm_amt' => $row['itm_amt'],
'dis_rt' => $row['dis_rt'],
'buy_amt' => $row['buy_amt'],
'vat_amt' => $row['vat_amt'],
'sum_amt' => $row['sum_amt'],
'end_dt' => $end_dt,
'ok_yn' => $row['ok_yn'],
'rmks' => $row['rmks'] ?? '',
'mid' => 'ADMIN'
]);
}
}
$pdo->commit();
echo json_encode([
'status' => 'success'
]);
exit;
}
throw new Exception('Invalid action');
} catch (Exception $e) {
if ($pdo->inTransaction()) {
$pdo->rollBack();
}
http_response_code(500);
echo json_encode([
'status' => 'error',
'message' => $e->getMessage()
]);
}

View File

@@ -0,0 +1,41 @@
<?php
// 1. DB 연결
require_once 'db_conn.php';
header('Content-Type: application/json');
try {
// JS의 URLSearchParams에서 보낸 키값과 일치시켜야 합니다.
$p_member_id = $_POST['member_id'] ?? '';
$p_user_nm = $_POST['user_nm'] ?? '';
$p_dept_nm = $_POST['dept_nm'] ?? '';
// 2. 프로시저 호출
// PostgreSQL 함수 호출 시 파라미터가 비어있으면 전체 조회가 되도록 설계됨
$stmt = $pdo->prepare("SELECT * FROM kngil.sp_use_history(?, ?, ?)");
$stmt->execute([
$p_member_id,
$p_user_nm,
$p_dept_nm
]);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
// 3. W2UI를 위해 각 행에 'recid' 필드 주입
$records = [];
foreach ($rows as $index => $row) {
// user_id와 sq_no 조합으로 유일키 생성 (권장)
// 만약 데이터가 없을 경우를 대비해 $index를 활용할 수도 있음
$row['recid'] = ($row['user_id'] ?? 'unknown') . '_' . ($row['sq_no'] ?? $index);
$records[] = $row;
}
// 4. 결과 출력
echo json_encode($records, JSON_UNESCAPED_UNICODE); // 한글 깨짐 방지
} catch (PDOException $e) {
http_response_code(500);
echo json_encode(['status' => 'error', 'message' => $e->getMessage()]);
}
?>

13
kngil/bbs/db_conn.php Normal file
View File

@@ -0,0 +1,13 @@
<?php
$dsn = "pgsql:host=172.16.9.44;port=5432;dbname=KNGIL";
$user = "postgres";
$pass = "erpteam1!";
try {
$pdo = new PDO($dsn, $user, $pass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
]);
// echo "PostgreSQL 연결 성공 🎉";
} catch (PDOException $e) {
echo $e->getMessage();
}

23
kngil/bbs/get_base_cd.php Normal file
View File

@@ -0,0 +1,23 @@
<?php
require_once 'db_conn.php';
header('Content-Type: application/json');
// URL 파라미터로 group_id를 받습니다. (예: get_base_cd.php?group_id=BS210)
$group_id = isset($_GET['group_id']) ? $_GET['group_id'] : '';
if (empty($group_id)) {
echo json_encode([]);
exit;
}
try {
// 파라미터(? 처리)를 사용하여 보안을 강화하고 재사용성을 높입니다.
$stmt = $pdo->prepare("SELECT id, text FROM kngil.fn_base_cd(?)");
$stmt->execute([$group_id]);
$items = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo json_encode($items);
} catch (PDOException $e) {
echo json_encode(['status' => 'error', 'message' => $e->getMessage()]);
}
?>

160
kngil/bbs/join copy.php Normal file
View File

@@ -0,0 +1,160 @@
<?php
require_once $_SERVER['DOCUMENT_ROOT'].'/kngil/bbs/db_conn.php';
header('Content-Type: application/json');
$data = json_decode(file_get_contents('php://input'), true);
$action = $data['action'] ?? '';
/* =================================================
아이디 중복확인 (fn_user_id_check 사용)
================================================= */
if ($action === 'check_id') {
$userId = trim($data['userId'] ?? '');
if (!preg_match('/^[a-zA-Z][a-zA-Z0-9]{3,11}$/', $userId)) {
echo json_encode([
'available' => false,
'message' => '아이디 형식 오류'
]);
exit;
}
$stmt = $pdo->prepare("
SELECT kngil.fn_user_id_check(:user_id)
");
$stmt->execute([
':user_id' => $userId
]);
$result = trim($stmt->fetchColumn());
if (strpos($result, 'SUCCESS') === 0) {
echo json_encode([
'available' => true,
'message' => '사용 가능한 아이디입니다.'
]);
} else {
echo json_encode([
'available' => false,
'message' => '이미 존재하는 아이디입니다.'
]);
}
exit;
}
/* =================================================
1. 필수값 검증
================================================= */
$required = [
'memberType', // 회원유형
'userId',
'password',
'userName',
'email',
'phone'
];
foreach ($required as $k) {
if (empty($data[$k])) {
echo json_encode([
'success' => false,
'message' => '필수 항목이 누락되었습니다.'
]);
exit;
}
}
/* =================================================
2. 회원유형 → co_bc 매핑
================================================= */
/*
기업회원 : '1'
개인회원 : '2'
→ 실제 코드값은 여기서 통제
*/
$co_bc = ($data['memberType'] === '1')
? 'CB100100' // 기업
: 'CB100200'; // 개인
/* =================================================
3. 비밀번호 규칙 + 암호화
================================================= */
if (!preg_match('/^(?=.*[A-Za-z])(?=.*\d)(?=.*[!@#$%^&*]).{12,}$/', $data['password'])) {
echo json_encode([
'success' => false,
'message' => '비밀번호 규칙이 올바르지 않습니다.'
]);
exit;
}
$hashedPw = password_hash($data['password'], PASSWORD_DEFAULT);
/* =================================================
4. 이메일 형식
================================================= */
if (!filter_var($data['email'], FILTER_VALIDATE_EMAIL)) {
echo json_encode([
'success' => false,
'message' => '이메일 형식 오류'
]);
exit;
}
/* =================================================
5. 프로시저 호출
================================================= */
try {
$stmt = $pdo->prepare("
SELECT kngil.sp_member_i(
:p_co_bc,
:p_member_id,
:p_user_pw,
:p_member_nm,
:p_email,
:p_tel_no,
:p_co_nm,
:p_dept_nm,
:p_cid
) AS result
");
$stmt->execute([
':p_co_bc' => $co_bc,
':p_member_id' => $data['userId'],
':p_user_pw' => $hashedPw,
':p_member_nm' => $data['userName'],
':p_email' => $data['email'],
':p_tel_no' => $data['phone'],
':p_co_nm' => $data['company'] ?? null,
':p_dept_nm' => $data['department'] ?? null,
':p_cid' => $data['userId']
]);
$result = $stmt->fetchColumn();
if ($result === 'SUCCESS') {
echo json_encode([
'success' => true
]);
} else {
echo json_encode([
'success' => false,
'message' => $result
]);
}
} catch (Exception $e) {
echo json_encode([
'success' => false,
'message' => '서버 오류'
]);
}
echo json_encode([
'success' => false,
'message' => 'Invalid action'
]);
exit;

203
kngil/bbs/join.php Normal file
View File

@@ -0,0 +1,203 @@
<?php
require_once $_SERVER['DOCUMENT_ROOT'].'/kngil/bbs/db_conn.php';
header('Content-Type: application/json');
/* =========================
로그 함수
========================= */
function join_log($label, $data = null) {
$logFile = $_SERVER['DOCUMENT_ROOT'].'/kngil/log/join.log';
$time = date('Y-m-d H:i:s');
$log = "[$time] $label\n";
if ($data !== null) {
$log .= print_r($data, true);
}
$log .= "\n-----------------------------\n";
file_put_contents($logFile, $log, FILE_APPEND);
}
/* =========================
JSON 입력 파싱
========================= */
$raw = file_get_contents('php://input');
$data = json_decode($raw, true);
join_log('RAW INPUT', $raw);
join_log('PARSED DATA', $data);
$action = $data['action'] ?? null;
join_log('ACTION', $action);
if (!$action) {
echo json_encode([
'success' => false,
'message' => 'Invalid action'
]);
exit;
}
/* =================================================
1. 아이디 중복확인
================================================= */
if ($action === 'check_id') {
$userId = trim($data['userId'] ?? '');
join_log('CHECK_ID userId', $userId);
if (!preg_match('/^[a-zA-Z][a-zA-Z0-9]{3,11}$/', $userId)) {
echo json_encode([
'available' => false,
'message' => '아이디 형식 오류'
]);
exit;
}
$stmt = $pdo->prepare("
SELECT kngil.fn_user_id_check(:user_id)
");
$stmt->execute([':user_id' => $userId]);
$result = trim($stmt->fetchColumn());
join_log('CHECK_ID RESULT', $result);
if (strpos($result, 'SUCCESS') === 0) {
echo json_encode([
'available' => true,
'message' => '사용 가능한 아이디입니다.'
]);
} else {
echo json_encode([
'available' => false,
'message' => '이미 존재하는 아이디입니다.'
]);
}
exit;
}
/* =================================================
2. 회원가입
================================================= */
if ($action !== 'signup') {
echo json_encode([
'success' => false,
'message' => 'Invalid action'
]);
exit;
}
/* =================================================
3. 필수값 검증
================================================= */
$required = ['memberType','userId','password','userName','email','phone'];
foreach ($required as $k) {
if (empty($data[$k])) {
join_log('REQUIRED MISSING', $k);
echo json_encode([
'success' => false,
'message' => '필수 항목이 누락되었습니다.'
]);
exit;
}
}
/* =================================================
4. 회원유형 → co_bc
================================================= */
$memberType = $data['memberType'] ?? '2';
$co_bc = ($memberType === '1') ? 'CB100100' : 'CB100200';
join_log('co_bc', $co_bc);
/* =================================================
5. 비밀번호 규칙 (8자)
================================================= */
if (!preg_match('/^(?=.*[A-Za-z])(?=.*\d)(?=.*[!@#$%^&*]).{8,}$/', $data['password'])) {
join_log('PASSWORD INVALID');
echo json_encode([
'success' => false,
'message' => '비밀번호 규칙이 올바르지 않습니다.'
]);
exit;
}
$userPw = $data['password'];
/* =================================================
6. 이메일 검증
================================================= */
if (!filter_var($data['email'], FILTER_VALIDATE_EMAIL)) {
join_log('EMAIL INVALID', $data['email']);
echo json_encode([
'success' => false,
'message' => '이메일 형식 오류'
]);
exit;
}
/* =================================================
7. 프로시저 호출
================================================= */
try {
join_log('SIGNUP PARAMS', [
'memberType' => $memberType,
'userId' => $data['userId'],
'userName' => $data['userName'],
'email' => $data['email'],
'phone' => $data['phone'],
'company' => $data['company'] ?? null,
'department' => $data['department'] ?? null,
]);
$stmt = $pdo->prepare("
SELECT kngil.sp_member_i(
:p_co_bc,
:p_member_id,
:p_user_pw,
:p_member_nm,
:p_email,
:p_tel_no,
:p_co_nm,
:p_dept_nm,
:p_cid
)
");
$stmt->execute([
':p_co_bc' => $co_bc,
':p_member_id' => $data['userId'],
':p_user_pw' => $userPw,
':p_member_nm' => $data['userName'],
':p_email' => $data['email'],
':p_tel_no' => $data['phone'],
':p_co_nm' => $data['company'] ?? null,
':p_dept_nm' => $data['department'] ?? null,
':p_cid' => $data['userId']
]);
$result = trim($stmt->fetchColumn());
join_log('PROC RESULT', $result);
if ($result === 'SUCCESS') {
join_log('SIGNUP SUCCESS', $data['userId']);
echo json_encode(['success' => true]);
} else {
join_log('SIGNUP FAIL', $result);
echo json_encode([
'success' => false,
'message' => $result
]);
}
} catch (Throwable $e) {
join_log('EXCEPTION', $e->getMessage());
echo json_encode([
'success' => false,
'message' => '서버 오류'
]);
}
exit;

68
kngil/bbs/login.php Normal file
View File

@@ -0,0 +1,68 @@
<?php
session_start();
require_once $_SERVER['DOCUMENT_ROOT'].'/kngil/bbs/db_conn.php';
$input = json_decode(file_get_contents('php://input'), true);
$id = trim($input['id'] ?? '');
$pw = trim($input['pw'] ?? '');
if ($id === '' || $pw === '') {
echo json_encode([
'status' => 'error',
'message' => '아이디와 비밀번호를 입력하세요.'
]);
exit;
}
$stmt = $pdo->prepare("
SELECT
u.member_id,
u.user_id,
u.user_pw,
u.user_nm,
u.auth_bc,
u.dept_nm,
m.co_nm,
u.tel_no,
u.email
FROM kngil.users u
LEFT JOIN kngil.members m
ON u.member_id = m.member_id
WHERE LOWER(u.user_id) = LOWER(:id)
AND u.use_yn = 'Y'
LIMIT 1
");
$stmt->execute([':id' => $id]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$user) {
echo json_encode([
'status' => 'error',
'message' => '존재하지 않는 아이디입니다.'
]);
exit;
}
// 현재 구조상 평문 비교
if ($user['user_pw'] !== $pw) {
echo json_encode([
'status' => 'error',
'message' => '비밀번호가 올바르지 않습니다.'
]);
exit;
}
// ✅ 로그인 성공
$_SESSION['login'] = [
'member_id' => $user['member_id'],
'user_id' => $user['user_id'],
'user_nm' => $user['user_nm'],
'auth_bc' => $user['auth_bc'],
'co_nm' => $user['co_nm'] ?? null,
'dept_nm' => $user['dept_nm'] ?? null,
'tel_no' => $user['tel_no'] ?? null,
'email' => $user['email'] ?? null
];
echo json_encode(['status' => 'success']);

View File

@@ -0,0 +1,179 @@
<?php
/**
* Sentinel SMS 매직링크 테스트 (PHP)
* - phoneNumber: 01086270921 (고정)
* - system: kngil
* - secret_key: MY_SECRET_KEY
*/
header('Content-Type: application/json; charset=utf-8');
/* =========================
설정값
========================= */
$AUTH_SERVER = 'http://61.98.205.242:8075';
$SYSTEM = 'kngil';
$SECRET_KEY = '9f3b2e7a0a4f1f25c41c8c2367d04d54a89a2a5b2b189d63a99a0b874db4b27f';
$PHONE = '01086270921';
/* =========================
JWT 생성 (HS256)
========================= */
function base64url_encode($data) {
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}
function create_jwt($payload, $secret) {
$header = ['alg'=>'HS256','typ'=>'JWT'];
$segments = [];
$segments[] = base64url_encode(json_encode($header));
$segments[] = base64url_encode(json_encode($payload));
$signing_input = implode('.', $segments);
$signature = hash_hmac('sha256', $signing_input, $secret, true);
$segments[] = base64url_encode($signature);
return implode('.', $segments);
}
/* =========================
cURL 요청 함수
========================= */
function curl_json($url, $method='GET', $headers=[], $body=null) {
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_POSTFIELDS => $body,
CURLOPT_TIMEOUT => 10
]);
$response = curl_exec($ch);
$err = curl_error($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($err) {
throw new Exception($err);
}
return [$code, $response];
}
/* =========================
MODE 분기
========================= */
$mode = $_GET['mode'] ?? 'request';
try {
/* =========================
1⃣ 매직링크 발급 요청
========================= */
if ($mode === 'request') {
// JWT payload (3분 유효)
$payload = [
'system' => $SYSTEM,
'iat' => time(),
'exp' => time() + 180
];
$jwt = create_jwt($payload, $SECRET_KEY);
[$code, $res] = curl_json(
$AUTH_SERVER.'/auth/sentinel',
'POST',
[
'Authorization: Bearer '.$jwt,
'Content-Type: application/json'
],
json_encode([
'phoneNumber' => $PHONE
])
);
echo json_encode([
'step' => 'sentinel_request',
'http_code' => $code,
'response' => json_decode($res, true)
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
exit;
}
/* =========================
2⃣ 매직링크 상태 확인
========================= */
if ($mode === 'status') {
$token = $_GET['token'] ?? '';
if (!$token) {
throw new Exception('token 필요');
}
$payload = [
'system' => $SYSTEM,
'iat' => time(),
'exp' => time() + 180
];
$jwt = create_jwt($payload, $SECRET_KEY);
[$code, $res] = curl_json(
$AUTH_SERVER.'/auth/status?token='.$token,
'GET',
[
'Authorization: Bearer '.$jwt
]
);
$data = json_decode($res, true);
// 🔴 여기부터가 "로그인 처리"
if (!empty($data['loggedIn'])) {
$stmt = $pdo->prepare("
SELECT member_id, user_id, user_nm, auth_bc
FROM kngil.users
WHERE REPLACE(tel_no, '-', '') = :phone
AND use_yn = 'Y'
LIMIT 1
");
$stmt->execute([':phone' => $PHONE]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$user) {
throw new Exception('해당 번호로 등록된 사용자 없음');
}
$_SESSION['login'] = [
'member_id' => $user['member_id'],
'user_id' => $user['user_id'],
'user_nm' => $user['user_nm'],
'auth_bc' => $user['auth_bc']
];
echo json_encode([
'status' => 'success',
'message' => '자동 로그인 완료'
]);
exit;
}
echo json_encode([
'status' => 'pending'
]);
exit;
}
} catch (Exception $e) {
echo json_encode([
'error' => true,
'message' => $e->getMessage()
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
}

212
kngil/bbs/login_sms.php Normal file
View File

@@ -0,0 +1,212 @@
<?php
/**
* Sentinel SMS 매직링크 로그인 (PHP)
* - system: kngil
* - secret_key: MY_SECRET_KEY
*/
session_start();
require_once $_SERVER['DOCUMENT_ROOT'].'/kngil/bbs/db_conn.php';
header('Content-Type: application/json; charset=utf-8');
/* =========================
설정값
========================= */
$AUTH_SERVER = 'http://61.98.205.242:8075';
$SYSTEM = 'kngil';
$SECRET_KEY = '9f3b2e7a0a4f1f25c41c8c2367d04d54a89a2a5b2b189d63a99a0b874db4b27f';
/* =========================
입력 처리
========================= */
$input = json_decode(file_get_contents('php://input'), true) ?? [];
$mode = $_GET['mode'] ?? $input['mode'] ?? '';
/* =========================
JWT 생성 (HS256)
========================= */
function base64url_encode($data) {
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}
function create_jwt($payload, $secret) {
$header = ['alg' => 'HS256', 'typ' => 'JWT'];
$segments = [];
$segments[] = base64url_encode(json_encode($header));
$segments[] = base64url_encode(json_encode($payload));
$signing_input = implode('.', $segments);
$signature = hash_hmac('sha256', $signing_input, $secret, true);
$segments[] = base64url_encode($signature);
return implode('.', $segments);
}
/* =========================
cURL JSON 요청
========================= */
function curl_json($url, $method = 'GET', $headers = [], $body = null) {
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_POSTFIELDS => $body,
CURLOPT_TIMEOUT => 10
]);
$response = curl_exec($ch);
$err = curl_error($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($err) {
throw new Exception($err);
}
return [$code, $response];
}
try {
/* =========================
1⃣ 매직링크 발급 요청
========================= */
if ($mode === 'request') {
$rawPhone = $input['phone'] ?? '';
$phone = preg_replace('/[^0-9]/', '', $rawPhone);
if (!$phone) {
echo json_encode([
'status' => 'error',
'message' => '휴대폰 번호를 입력하세요.'
]);
exit;
}
// JWT payload (3분 유효)
$payload = [
'system' => $SYSTEM,
'iat' => time(),
'exp' => time() + 180
];
$jwt = create_jwt($payload, $SECRET_KEY);
[$code, $res] = curl_json(
$AUTH_SERVER . '/auth/sentinel',
'POST',
[
'Authorization: Bearer ' . $jwt,
'Content-Type: application/json'
],
json_encode([
'phoneNumber' => $phone
])
);
$result = json_decode($res, true);
if (empty($result['token'])) {
throw new Exception('Sentinel token 발급 실패');
}
// ✅ token 반드시 저장
$_SESSION['sms_login'] = [
'phone' => $phone,
'token' => $result['token']
];
echo json_encode([
'status' => 'success'
]);
exit;
}
/* =========================
2⃣ 매직링크 상태 확인 → 자동 로그인
========================= */
if ($mode === 'status') {
if (
empty($_SESSION['sms_login']['token']) ||
empty($_SESSION['sms_login']['phone'])
) {
echo json_encode(['status' => 'pending']);
exit;
}
$token = $_SESSION['sms_login']['token'];
$phone = $_SESSION['sms_login']['phone'];
$payload = [
'system' => $SYSTEM,
'iat' => time(),
'exp' => time() + 180
];
$jwt = create_jwt($payload, $SECRET_KEY);
[$code, $res] = curl_json(
$AUTH_SERVER . '/auth/status?token=' . $token,
'GET',
[
'Authorization: Bearer ' . $jwt
]
);
$data = json_decode($res, true);
// 🔴 인증 완료 시 로그인 처리
if (!empty($data['loggedIn'])) {
$stmt = $pdo->prepare("
SELECT
member_id,
user_id,
user_nm,
auth_bc
FROM kngil.users
WHERE REPLACE(tel_no, '-', '') = :phone
AND use_yn = 'Y'
LIMIT 1
");
$stmt->execute([':phone' => $phone]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$user) {
throw new Exception('해당 번호로 등록된 사용자 없음');
}
// ✅ 기존 PW 로그인과 동일
$_SESSION['login'] = [
'member_id' => $user['member_id'],
'user_id' => $user['user_id'],
'user_nm' => $user['user_nm'],
'auth_bc' => $user['auth_bc']
];
unset($_SESSION['sms_login']);
echo json_encode([
'status' => 'success',
'message' => '자동 로그인 완료'
], JSON_UNESCAPED_UNICODE);
exit;
}
echo json_encode(['status' => 'pending']);
exit;
}
} catch (Exception $e) {
echo json_encode([
'error' => true,
'message' => $e->getMessage()
], JSON_UNESCAPED_UNICODE);
}

6
kngil/bbs/logout.php Normal file
View File

@@ -0,0 +1,6 @@
<?php
session_start();
session_destroy();
header('Location: /kngil/skin/index.php');
exit;

40
kngil/bbs/mypage01.php Normal file
View File

@@ -0,0 +1,40 @@
<?php
//mypage01.php
session_start();
require_once $_SERVER['DOCUMENT_ROOT'].'/kngil/bbs/db_conn.php';
if (!isset($_SESSION['login'])) {
http_response_code(401);
echo json_encode(['status' => 'error', 'message' => '로그인이 필요합니다.']);
exit;
}
$input = json_decode(file_get_contents('php://input'), true);
$pw = trim($input['pw'] ?? '');
if ($pw === '') {
echo json_encode(['status' => 'error', 'message' => '비밀번호를 입력하세요.']);
exit;
}
$userId = $_SESSION['login']['user_id'];
$stmt = $pdo->prepare("
SELECT user_pw
FROM kngil.users
WHERE user_id = :user_id
AND use_yn = 'Y'
");
$stmt->execute([':user_id' => $userId]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$user || $user['user_pw'] !== $pw) {
echo json_encode(['status' => 'error', 'message' => '비밀번호가 올바르지 않습니다.']);
exit;
}
// ✅ 비밀번호 재인증 성공
$_SESSION['mypage_verified'] = true;
echo json_encode(['status' => 'success']);

90
kngil/bbs/mypage02.php Normal file
View File

@@ -0,0 +1,90 @@
<?php
session_start();
require_once $_SERVER['DOCUMENT_ROOT'].'/kngil/bbs/db_conn.php';
header('Content-Type: application/json');
if (!isset($_SESSION['login'])) {
http_response_code(401);
echo json_encode(['status' => 'error', 'message' => '로그인이 필요합니다.']);
exit;
}
if (empty($_SESSION['mypage_verified'])) {
http_response_code(403);
echo json_encode(['status' => 'error', 'message' => '마이페이지 인증이 필요합니다.']);
exit;
}
$userId = $_SESSION['login']['user_id'];
/* =========================
페이징 파라미터
========================= */
$page = max(1, intval($_GET['page'] ?? 1));
$pageSize = 5;
$offset = ($page - 1) * $pageSize;
try {
/* =========================
1. 내 정보
========================= */
$stmt = $pdo->prepare("SELECT * FROM kngil.sp_users_my_r(:user_id)");
$stmt->execute([':user_id' => $userId]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$user) {
echo json_encode(['status' => 'error', 'message' => '사용자 정보 없음']);
exit;
}
/* =========================
2. 전체 건수
========================= */
$cntStmt = $pdo->prepare("
SELECT COUNT(*)
FROM kngil.use_history
WHERE user_id = :user_id
");
$cntStmt->execute([':user_id' => $userId]);
$totalCount = (int)$cntStmt->fetchColumn();
$totalPages = (int)ceil($totalCount / $pageSize);
/* =========================
3. 현재 페이지 데이터
========================= */
$histStmt = $pdo->prepare("
SELECT *
FROM kngil.sp_users_my_history(:user_id)
OFFSET :offset
LIMIT :limit
");
$histStmt->bindValue(':user_id', $userId, PDO::PARAM_STR);
$histStmt->bindValue(':offset', $offset, PDO::PARAM_INT);
$histStmt->bindValue(':limit', $pageSize, PDO::PARAM_INT);
$histStmt->execute();
$history = $histStmt->fetchAll(PDO::FETCH_ASSOC);
echo json_encode([
'status' => 'success',
'user' => $user,
'history' => $history,
'pagination' => [
'page' => $page,
'pageSize' => $pageSize,
'totalCount' => $totalCount,
'totalPages' => $totalPages
]
]);
} catch (Exception $e) {
http_response_code(500);
echo json_encode([
'status' => 'error',
'message' => '서버 오류',
'detail' => $e->getMessage()
]);
}

139
kngil/bbs/mypage03.php Normal file
View File

@@ -0,0 +1,139 @@
<?php
session_start();
require_once $_SERVER['DOCUMENT_ROOT'].'/kngil/bbs/db_conn.php';
header('Content-Type: application/json');
// 로그인 체크
if (!isset($_SESSION['login'])) {
http_response_code(401);
echo json_encode(['status' => 'error', 'message' => '로그인이 필요합니다.']);
exit;
}
// 2차 인증 체크
if (empty($_SESSION['mypage_verified'])) {
http_response_code(403);
echo json_encode(['status' => 'error', 'message' => '마이페이지 인증이 필요합니다.']);
exit;
}
$userId = $_SESSION['login']['user_id'];
$memberId = $_SESSION['login']['member_id'];
/* ==================================================
GET : 회원정보 조회 (mypage03 열릴 때)
================================================== */
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
try {
$stmt = $pdo->prepare("
SELECT *
FROM kngil.sp_users_r(
:member_id,
'',
'',
'Y'
)
WHERE user_id = :user_id
LIMIT 1
");
$stmt->execute([
':member_id' => $memberId,
':user_id' => $userId
]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$row) {
echo json_encode(['status' => 'error', 'message' => '회원정보를 찾을 수 없습니다.']);
exit;
}
echo json_encode([
'status' => 'success',
'data' => $row
]);
exit;
} catch (Exception $e) {
http_response_code(500);
echo json_encode([
'status' => 'error',
'message' => '조회 중 오류 발생',
'detail' => $e->getMessage()
]);
exit;
}
}
/* ==================================================
POST : 회원정보 수정
================================================== */
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$input = json_decode(file_get_contents('php://input'), true);
$userPw = trim($input['password'] ?? '');
$email = trim($input['email'] ?? '');
$deptNm = trim($input['dept_nm'] ?? '');
// 전화번호는 이번 단계에서 수정 안 함
$tel_no = trim($input['tel_no'] ?? '');
if ($email === '') {
echo json_encode(['status' => 'error', 'message' => '이메일은 필수입니다.']);
exit;
}
if ($userPw !== '' && strlen($userPw) < 8) {
echo json_encode(['status' => 'error', 'message' => '비밀번호는 8자 이상이어야 합니다.']);
exit;
}
try {
$stmt = $pdo->prepare("
SELECT kngil.sp_users_my_u(
:user_id,
:user_pw,
:email,
:tel_no,
:dept_nm,
:mid
)
");
$stmt->execute([
':user_id' => $userId,
':user_pw' => $userPw,
':email' => $email,
':tel_no' => $tel_no,
':dept_nm' => $deptNm,
':mid' => $userId
]);
$result = $stmt->fetchColumn();
if ($result !== 'SUCCESS') {
echo json_encode(['status' => 'error', 'message' => $result]);
exit;
}
echo json_encode(['status' => 'success']);
exit;
} catch (Exception $e) {
http_response_code(500);
echo json_encode([
'status' => 'error',
'message' => '저장 중 오류 발생',
'detail' => $e->getMessage()
]);
exit;
}
}
// 허용되지 않은 메서드
http_response_code(405);
echo json_encode(['status' => 'error', 'message' => 'Invalid request']);

110
kngil/bbs/qa_comment.php Normal file
View File

@@ -0,0 +1,110 @@
<?php
session_start();
header('Content-Type: application/json');
if (empty($_SESSION['login'])) {
echo json_encode(['status'=>'error','message'=>'로그인 필요']);
exit;
}
require_once __DIR__.'/db_conn.php';
$postId = (int)($_POST['postId'] ?? 0);
$content = trim($_POST['comment'] ?? '');
if ($postId < 1 || ($content === '' && empty($_FILES['images']))) {
echo json_encode(['status'=>'error','message'=>'내용 없음']);
exit;
}
$user = $_SESSION['login'];
try {
$pdo->beginTransaction();
/* =========================
1) 댓글 INSERT
========================= */
$stmt = $pdo->prepare("
INSERT INTO kngil.qa_comments
(post_id, commenter, user_nm, content, cdt_dt)
VALUES
(:post_id, :commenter, :user_nm, :content, NOW())
RETURNING comment_id
");
$stmt->execute([
':post_id' => $postId,
':commenter' => $user['user_id'],
':user_nm' => $user['user_nm'],
':content' => $content
]);
$commentId = $stmt->fetchColumn();
/* =========================
2) 이미지 업로드
========================= */
$images = [];
if (!empty($_FILES['images']['name'][0])) {
$uploadDir = $_SERVER['DOCUMENT_ROOT'].'/kngil/uploads/comment/';
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0777, true);
}
foreach ($_FILES['images']['name'] as $i => $name) {
if ($_FILES['images']['error'][$i] !== UPLOAD_ERR_OK) continue;
$tmp = $_FILES['images']['tmp_name'][$i];
$size = $_FILES['images']['size'][$i];
$ext = strtolower(pathinfo($name, PATHINFO_EXTENSION));
$save = time().'_'.bin2hex(random_bytes(4)).'.'.$ext;
$fullPath = $uploadDir.$save;
move_uploaded_file($tmp, $fullPath);
$path = '/kngil/uploads/comment/'.$save;
// 🔥 DB 구조에 맞게 INSERT
$pdo->prepare("
INSERT INTO kngil.qa_comment_images
(comment_id, file_name, file_path, thumb_path, file_size, uploaded_at)
VALUES
(:comment_id, :file_name, :file_path, :thumb_path, :file_size, NOW())
")->execute([
':comment_id' => $commentId,
':file_name' => $name,
':file_path' => $path,
':thumb_path' => $path, // 지금은 동일 (추후 썸네일 분리 가능)
':file_size' => $size
]);
$images[] = [
'thumb' => $path,
'full' => $path,
'name' => $name
];
}
}
$pdo->commit();
echo json_encode([
'status' => 'ok',
'comment_id' => $commentId,
'comment_text' => $content,
'user_name' => $user['user_nm'],
'login_id' => $user['user_id'],
'created_at' => date('Y-m-d H:i'),
'images' => $images
]);
} catch (Exception $e) {
$pdo->rollBack();
echo json_encode([
'status' => 'error',
'message' => $e->getMessage()
]);
}

View File

@@ -0,0 +1,33 @@
<?php
session_start();
header('Content-Type: application/json');
require_once __DIR__.'/db_conn.php';
if (empty($_SESSION['login'])) {
echo json_encode(['status'=>'error','message'=>'로그인 필요']);
exit;
}
$id = (int)($_POST['commentId'] ?? 0);
$userId = $_SESSION['login']['user_id'];
if ($id < 1) {
echo json_encode(['status'=>'error','message'=>'잘못된 요청']);
exit;
}
// 관리자 or 본인
$isAdmin = function_exists('is_qna_admin') && is_qna_admin();
$sql = $isAdmin
? "DELETE FROM kngil.qa_comments WHERE comment_id = :id"
: "DELETE FROM kngil.qa_comments WHERE comment_id = :id AND commenter = :user";
$stmt = $pdo->prepare($sql);
$params = [':id'=>$id];
if (!$isAdmin) $params[':user'] = $userId;
$stmt->execute($params);
echo json_encode(['status'=>'ok']);

View File

@@ -0,0 +1,34 @@
<?php
session_start();
header('Content-Type: application/json');
require_once __DIR__.'/db_conn.php';
if (empty($_SESSION['login'])) {
echo json_encode(['status'=>'error','message'=>'로그인 필요']);
exit;
}
$id = (int)($_POST['commentId'] ?? 0);
$content = trim($_POST['comment'] ?? '');
if ($id < 1 || $content === '') {
echo json_encode(['status'=>'error','message'=>'잘못된 요청']);
exit;
}
// 본인 댓글만 수정
$stmt = $pdo->prepare("
UPDATE kngil.qa_comments
SET content = :content,
mdt_dt = NOW()
WHERE comment_id = :id
AND commenter = :user
");
$stmt->execute([
':content' => $content,
':id' => $id,
':user' => $_SESSION['login']['user_id']
]);
echo json_encode(['status'=>'ok']);

233
kngil/bbs/qa_detail.php Normal file
View File

@@ -0,0 +1,233 @@
<?php
// kngil/bbs/qa_detail.php
ini_set('display_errors', 1);
error_reporting(E_ALL);
/* ===============================
1. 세션 & 로그인 체크
=============================== */
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
if (empty($_SESSION['login'])) {
echo "<script>
alert('로그인 후 이용 가능합니다.');
location.href = '/kngil/skin/qa_list.skin.php';
</script>";
exit;
}
$login = $_SESSION['login'];
$me = $login['user_id'] ?? '';
$auth = $login['auth_bc'] ?? '';
$isAdmin = in_array($auth, ['BS100100', 'BS100200']); // 개발자/관리자
/* ===============================
2. DB 연결 (PostgreSQL)
=============================== */
require_once $_SERVER['DOCUMENT_ROOT'].'/kngil/bbs/db_conn.php';
/* ===============================
4. 삭제 처리
=============================== */
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'delete') {
$postId = (int)($_POST['post_id'] ?? 0);
if ($postId < 1) {
die('잘못된 요청입니다.');
}
// 글 조회
$stmt = $pdo->prepare("
SELECT post_id, user_id, stat_bc
FROM kngil.qa_posts
WHERE post_id = :pid
");
$stmt->execute([':pid' => $postId]);
$post = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$post) {
die('존재하지 않는 글입니다.');
}
// 상태 제한 (검토중 / 완료면 삭제 불가)
if (in_array($post['stat_bc'], ['REVIEW', 'DONE']) && !$isAdmin) {
die('검토중이거나 답변 완료된 글은 삭제할 수 없습니다.');
}
// 권한 체크 (본인 or 관리자)
if (!$isAdmin && $post['user_id'] !== $loginUser['user_id']) {
die('삭제 권한이 없습니다.');
}
try {
$pdo->beginTransaction();
// 1⃣ 첨부파일 삭제
$fs = $pdo->prepare("
SELECT save_path
FROM kngil.qa_attachments
WHERE post_id = :pid
");
$fs->execute([':pid' => $postId]);
foreach ($fs->fetchAll() as $f) {
$file = $_SERVER['DOCUMENT_ROOT'] . $f['save_path'];
if (is_file($file)) unlink($file);
}
$pdo->prepare("DELETE FROM kngil.qa_attachments WHERE post_id = ?")
->execute([$postId]);
// 2⃣ 댓글 삭제
$pdo->prepare("DELETE FROM kngil.qa_comments WHERE post_id = ?")
->execute([$postId]);
// 3⃣ 본문 삭제
$pdo->prepare("DELETE FROM kngil.qa_posts WHERE post_id = ?")
->execute([$postId]);
$pdo->commit();
header("Location: /kngil/skin/qa_list.skin.php");
exit;
} catch (Exception $e) {
$pdo->rollBack();
die('삭제 중 오류 발생: ' . $e->getMessage());
}
}
/* ===============================
3. post_id 검증
=============================== */
$postId = (int)($_GET['id'] ?? 0);
if ($postId < 1) {
exit('잘못된 접근입니다.');
}
/* ===============================
5. 글 조회
=============================== */
$stmt = $pdo->prepare("
SELECT
p.post_id,
p.user_id,
p.user_nm,
p.tel_no,
p.category,
p.co_nm,
p.dept_nm,
p.title,
p.content,
p.attachment,
p.stat_bc,
p.is_secret,
p.complete_form,
p.cdt_dt,
p.mid_dt,
p.is_read_admin,
u.email
FROM kngil.qa_posts p
LEFT JOIN kngil.users u
ON p.user_id = u.user_id
WHERE p.post_id = :pid
");
$stmt->execute([':pid' => $postId]);
$post = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$post) {
exit('존재하지 않는 글입니다.');
}
/* ===============================
6. 비밀글 접근 제어
=============================== */
if ($post['is_secret'] === 'Y' && $post['user_id'] !== $me && !$isAdmin) {
exit('⚠️ 비밀글은 작성자 또는 관리자만 확인할 수 있습니다.');
}
/* ===============================
7. 관리자 열람 처리
=============================== */
if ($isAdmin && $post['is_read_admin'] === 'N') {
$pdo->prepare("
UPDATE kngil.qa_posts
SET is_read_admin = 'Y'
WHERE post_id = :pid
")->execute([':pid' => $postId]);
}
/* ===============================
8. 라벨 매핑
=============================== */
$STATUS_LABELS = [
'WAIT' => '문의접수',
'REVIEW'=> '검토중',
'DONE' => '답변완료'
];
$CATEGORY_LABELS = [
'general' => '일반문의',
'improvement' => '개선문의',
'error' => '오류문의',
'notice' => '공지사항'
];
$post['status_label'] = $STATUS_LABELS[$post['stat_bc']] ?? $post['stat_bc'];
$post['category_label'] = $CATEGORY_LABELS[$post['category']] ?? $post['category'];
$post['display_name'] = $post['user_nm'];
/* ===============================
9. 첨부파일 조회
=============================== */
$af = $pdo->prepare("
SELECT
id,
ori_name,
save_path,
file_size,
uploaded_at
FROM kngil.qa_attachments
WHERE post_id = :pid
ORDER BY id ASC
");
$af->execute([':pid' => $postId]);
$attachments = $af->fetchAll(PDO::FETCH_ASSOC);
/* ===============================
10. 댓글 조회 (일단 구조만)
=============================== */
$stmt = $pdo->prepare("
SELECT
comment_id,
post_id,
commenter,
content,
user_nm,
cdt_dt
FROM kngil.qa_comments
WHERE post_id = :post_id
ORDER BY cdt_dt ASC
");
$stmt->execute([
':post_id' => $postId
]);
$comments = $stmt->fetchAll(PDO::FETCH_ASSOC);
/* ===============================
11. 소유자 여부 (수정 버튼용)
=============================== */
$isOwner = ($post['user_id'] === $me);
/* ===============================
12. 스킨 렌더링
=============================== */
include $_SERVER['DOCUMENT_ROOT'].'/kngil/skin/qa_detail.skin.php';

152
kngil/bbs/qa_list.php Normal file
View File

@@ -0,0 +1,152 @@
<?php
/**
* Q&A 리스트 컨트롤러
* - 스킨: /kngil/skin/qa_list.skin.php
*/
ini_set('display_errors', 1);
error_reporting(E_ALL);
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
require_once $_SERVER['DOCUMENT_ROOT'].'/kngil/bbs/db_conn.php';
/* =========================
1. 입력값 정리
========================= */
$page = max(1, (int)($_GET['page'] ?? 1));
$pageSize = 15;
$offset = ($page - 1) * $pageSize;
$search = trim($_GET['q'] ?? '');
$cats = $_GET['cat'] ?? []; // 배열
$writer = $_GET['writer'] ?? ''; // me
$status = $_GET['status'] ?? 'all'; // 상태
$isLogin = !empty($_SESSION['login']);
$loginUserId = $_SESSION['login']['user_id'] ?? null;
/* =========================
2. WHERE 조건 구성
========================= */
$where = [];
$params = [];
/* 카테고리 */
if (!empty($cats) && !in_array('all', $cats, true)) {
$catKeys = [];
foreach ($cats as $i => $cat) {
$key = ":cat{$i}";
$catKeys[] = $key;
$params[$key] = $cat;
}
$where[] = "p.category IN (" . implode(',', $catKeys) . ")";
}
/* 내가 작성한 글 */
if ($writer === 'me' && $loginUserId) {
$where[] = "p.user_id = :me";
$params[':me'] = $loginUserId;
}
/* 상태 */
if ($status !== 'all') {
$where[] = "p.stat_bc = :status";
$params[':status'] = $status;
}
/* 검색 (제목 + 내용) */
if ($search !== '') {
$where[] = "(p.title ILIKE :q OR p.content ILIKE :q)";
$params[':q'] = "%{$search}%";
}
$whereSql = $where ? 'WHERE ' . implode(' AND ', $where) : '';
/* =========================
3. 전체 건수
========================= */
$countSql = "
SELECT COUNT(*)
FROM kngil.qa_posts p
{$whereSql}
";
$stmt = $pdo->prepare($countSql);
$stmt->execute($params);
$totalCount = (int)$stmt->fetchColumn();
/* =========================
4. 리스트 조회
========================= */
$listSql = "
SELECT
p.post_id,
p.category,
p.title,
p.user_nm,
p.co_nm,
p.dept_nm,
p.stat_bc AS status,
p.is_secret,
p.cdt_dt AS created_at,
-- 댓글 수
(
SELECT COUNT(*)
FROM kngil.qa_comments c
WHERE c.post_id = p.post_id
) AS comment_count,
-- 첨부파일 수
(
SELECT COUNT(*)
FROM kngil.qa_attachments a
WHERE a.post_id = p.post_id
) AS file_count
FROM kngil.qa_posts p
{$whereSql}
ORDER BY
(p.category = 'notice') DESC,
p.post_id DESC
LIMIT :limit OFFSET :offset
";
$stmt = $pdo->prepare($listSql);
/* 바인딩 */
foreach ($params as $k => $v) {
$stmt->bindValue($k, $v);
}
$stmt->bindValue(':limit', $pageSize, PDO::PARAM_INT);
$stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
$stmt->execute();
$posts = $stmt->fetchAll(PDO::FETCH_ASSOC);
/* =========================
5. 표시용 가공
========================= */
foreach ($posts as &$row) {
// 회사 표시
$row['display_company'] = $row['co_nm'] ?? '';
// 작성자 표시
$row['display_name'] = $row['user_nm'] ?? $row['user_id'];
// 날짜 포맷
$row['created_at'] = substr($row['created_at'], 0, 10);
}
unset($row);
/* =========================
6. 페이징 계산
========================= */
$totalPages = (int)ceil($totalCount / $pageSize);
/* =========================
7. 스킨 렌더링
========================= */
include $_SERVER['DOCUMENT_ROOT'].'/kngil/skin/qa_list.skin.php';

50
kngil/bbs/qa_status.php Normal file
View File

@@ -0,0 +1,50 @@
<?php
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
require_once $_SERVER['DOCUMENT_ROOT'].'/kngil/bbs/db_conn.php';
require_once $_SERVER['DOCUMENT_ROOT'].'/kngil/bbs/adm_guard.php';
/* =========================
1. 관리자 권한 체크
========================= */
if (!defined('IS_SUPER_ADMIN') || !IS_SUPER_ADMIN) {
http_response_code(403);
exit('권한이 없습니다.');
}
/* =========================
2. 파라미터 체크
========================= */
$postId = (int)($_POST['post_id'] ?? 0);
$status = $_POST['status'] ?? '';
$ALLOW_STATUS = ['new','review','deep','patch','done'];
if ($postId < 1 || !in_array($status, $ALLOW_STATUS, true)) {
exit('잘못된 요청입니다.');
}
/* =========================
3. 상태 업데이트
========================= */
try {
$stmt = $pdo->prepare("
UPDATE kngil.qa_posts
SET stat_bc = :status,
mid_dt = NOW()
WHERE post_id = :pid
");
$stmt->execute([
':status' => $status,
':pid' => $postId
]);
// 상세 페이지로 복귀
header("Location: /kngil/bbs/qa_detail.php?id={$postId}");
exit;
} catch (Exception $e) {
exit('DB 오류: '.$e->getMessage());
}

231
kngil/bbs/qa_write.php Normal file
View File

@@ -0,0 +1,231 @@
<?php
ini_set('display_errors', 1);
error_reporting(E_ALL);
/* ===============================
1. 세션 & 로그인 체크
=============================== */
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
// echo '<pre>';
// var_dump($_SESSION['login']);
// exit;
if (empty($_SESSION['login'])) {
echo "<script>
alert('로그인이 필요합니다.');
location.href = '/kngil/skin/qa_list.skin.php';
</script>";
exit;
}
$loginUser = $_SESSION['login'];
/* ===============================
2. DB 연결
=============================== */
require_once $_SERVER['DOCUMENT_ROOT'].'/kngil/bbs/db_conn.php';
/* ===============================
3. 수정 여부 판단
=============================== */
$postId = isset($_GET['id']) ? (int)$_GET['id'] : 0;
$isEdit = $postId > 0;
/* ===============================
4. 수정 모드 기존 글 로드
=============================== */
$post = [
'category' => '',
'title' => '',
'content' => '',
'is_secret' => 'N',
];
if ($isEdit) {
$stmt = $pdo->prepare("SELECT * FROM kngil.qa_posts WHERE post_id = :pid");
$stmt->execute([':pid' => $postId]);
$post = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$post) {
die('존재하지 않는 글입니다.');
}
// 작성자 본인만 수정 가능
if ($post['user_id'] !== ($loginUser['user_id'] ?? '')) {
die('수정 권한이 없습니다.');
}
}
/* ===============================
5. 첨부파일 업로드
=============================== */
function handle_file_uploads(PDO $pdo, int $postId)
{
if (empty($_FILES['attach']['name'][0])) return;
$uploadDir = $_SERVER['DOCUMENT_ROOT'] . '/kngil/uploads/qa/';
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0777, true);
}
$allowExt = ['jpg','jpeg','png','gif','pdf','hwp','doc','docx','xls','xlsx','zip'];
foreach ($_FILES['attach']['name'] as $i => $oriName) {
if ($_FILES['attach']['error'][$i] !== UPLOAD_ERR_OK) continue;
$tmp = $_FILES['attach']['tmp_name'][$i];
$size = $_FILES['attach']['size'][$i];
$ext = strtolower(pathinfo($oriName, PATHINFO_EXTENSION));
if (!in_array($ext, $allowExt)) continue;
if ($size > 30 * 1024 * 1024) continue;
$saveName = time() . '_' . bin2hex(random_bytes(6)) . '.' . $ext;
$savePath = $uploadDir . $saveName;
if (!move_uploaded_file($tmp, $savePath)) continue;
$stmt = $pdo->prepare("
INSERT INTO kngil.qa_attachments (
post_id,
ori_name,
save_path,
file_size,
uploaded_at
) VALUES (
:post_id,
:ori_name,
:save_path,
:file_size,
NOW()
)
");
$stmt->execute([
':post_id' => $postId,
':ori_name' => $oriName,
':save_path' => '/kngil/uploads/qa/' . $saveName,
':file_size' => $size
]);
}
}
/* ===============================
6. POST 처리 (등록 / 수정)
=============================== */
$errors = [];
$secret = 'N';
$category = '';
$title = '';
$content = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$category = trim($_POST['category'] ?? '');
$title = trim($_POST['title'] ?? '');
$content = trim($_POST['content'] ?? '');
$secret = isset($_POST['secret']) ? 'Y' : 'N';
if ($category === '') $errors[] = '구분을 선택하세요.';
if ($title === '') $errors[] = '제목을 입력하세요.';
if ($content === '') $errors[] = '내용을 입력하세요.';
// 첨부파일명만 저장 (실파일 저장은 추후 분리 가능)
$attachment = null;
if (!empty($_FILES['attach']['name'][0])) {
$attachment = implode(',', $_FILES['attach']['name']);
}
if (empty($errors)) {
try {
if ($isEdit) {
/* ---------- UPDATE ---------- */
$stmt = $pdo->prepare("
UPDATE kngil.qa_posts
SET category = :category,
title = :title,
content = :content,
is_secret = :is_secret,
mid_dt = NOW()
WHERE post_id = :pid
");
$stmt->execute([
':category' => $category,
':title' => $title,
':content' => $content,
':is_secret' => $secret, // 'Y' or 'N'
':pid' => $postId
]);
handle_file_uploads($pdo, $postId);
} else {
/* ---------- INSERT ---------- */
$stmt = $pdo->prepare("
INSERT INTO kngil.qa_posts (
user_id,
user_nm,
tel_no,
co_nm,
dept_nm,
category,
title,
content,
is_secret,
stat_bc,
is_read_admin,
cdt_dt
) VALUES (
:user_id,
:user_nm,
:tel_no,
:co_nm,
:dept_nm,
:category,
:title,
:content,
:is_secret,
'wait',
'N',
NOW()
)
RETURNING post_id
");
// var_dump($loginUser);
// exit;
$stmt->execute([
':user_id' => $loginUser['user_id'],
':user_nm' => $loginUser['user_nm'],
':tel_no' => $loginUser['tel_no'] ?? null,
':co_nm' => $loginUser['co_nm'] ?? null,
':dept_nm' => $loginUser['dept_nm'] ?? null,
':category' => $category,
':title' => $title,
':content' => $content,
':is_secret' => $secret
]);
$postId = $stmt->fetchColumn();
handle_file_uploads($pdo, $postId);
}
header("Location: /kngil/bbs/qa_detail.php?id={$postId}");
exit;
} catch (Exception $e) {
$errors[] = 'DB 오류: ' . $e->getMessage();
}
}
}
/* ===============================
7. 화면 출력
=============================== */
include $_SERVER['DOCUMENT_ROOT'].'/kngil/skin/qa_write.skin.php';

223
kngil/bbs/sales_results.php Normal file
View File

@@ -0,0 +1,223 @@
<?php
ini_set('display_errors', 1);
error_reporting(E_ALL);
header("Content-Type: application/json; charset=utf-8");
/* -----------------------------------------------------
🔵 DB 연결
----------------------------------------------------- */
try {
$pdo = new PDO(
"mysql:host=localhost;dbname=egbim;charset=utf8mb4",
"egbim",
"baron3840!!",
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
]
);
} catch (Exception $e) {
echo json_encode(["status" => "fail", "message" => "DB 연결 실패"]);
exit;
}
/* -----------------------------------------------------
🔵 공통 날짜 변환 함수 (MM/DD/YYYY → YYYY-MM-DD)
----------------------------------------------------- */
function normalize_date($dateStr) {
if (!$dateStr) return null;
// 이미 YYYY-MM-DD라면 그대로 반환
if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $dateStr)) {
return $dateStr;
}
// MM/DD/YYYY → YYYY-MM-DD (한자리/두자리 모두 허용)
if (preg_match('/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/', $dateStr, $m)) {
$month = str_pad($m[1], 2, '0', STR_PAD_LEFT);
$day = str_pad($m[2], 2, '0', STR_PAD_LEFT);
return "{$m[3]}-$month-$day";
}
// 형식 이상하면 null 리턴
return null;
}
/* -----------------------------------------------------
🔵 요청 액션
----------------------------------------------------- */
$action = $_POST['action'] ?? $_GET['action'] ?? "";
/* =====================================================
1) LIST
===================================================== */
if ($action === "list") {
$stmt = $pdo->query("
SELECT r.*, m.emp_name
FROM sales_results r
LEFT JOIN sales_members m ON r.emp_no = m.emp_no
ORDER BY r.seq_no DESC
");
echo json_encode([
"status" => "ok",
"records" => $stmt->fetchAll()
]);
exit;
}
/* =====================================================
2) INSERT (seq_no 자동 증가)
===================================================== */
if ($action === "insert") {
$sales_date = normalize_date($_POST['sales_date']);
if (!$sales_date) {
echo json_encode([
"status" => "error",
"message" => "실적일(sales_date) 형식 오류. YYYY-MM-DD 또는 MM/DD/YYYY 로 입력하세요."
]);
exit;
}
$next_seq = $pdo->query("SELECT IFNULL(MAX(seq_no), 0) + 1 FROM sales_results")->fetchColumn();
// 🔥 서버에서 총금액 계산
$qty = (int)($_POST['quantity'] ?? 0);
$unit = (int)($_POST['unit_price'] ?? 0);
$discount = (int)($_POST['discount'] ?? 0);
$total_amount = ($qty * $unit) - $discount;
if ($total_amount < 0) $total_amount = 0;
$stmt = $pdo->prepare("
INSERT INTO sales_results
(seq_no, sales_date, emp_no, client_code, product_code, quantity, unit_price, discount, total_amount, remarks)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
");
$stmt->execute([
$next_seq,
$sales_date,
$_POST['emp_no'],
$_POST['client_code'],
$_POST['product_code'],
$qty,
$unit,
$discount,
$total_amount, // 🔥 클라이언트 값 무시, 서버 계산값 넣기
$_POST['remarks']
]);
echo json_encode(["status" => "ok"]);
exit;
}
/* =====================================================
3) UPDATE
===================================================== */
if ($action === "update") {
$seq_no = $_POST['seq_no'] ?? '';
if (!$seq_no) {
echo json_encode(["status" => "error", "message" => "seq_no 누락"]);
exit;
}
unset($_POST['action'], $_POST['seq_no']);
/* -----------------------------
🔵 날짜 변환 (MM/DD/YYYY → YYYY-MM-DD)
----------------------------- */
if (!empty($_POST['sales_date'])) {
$date = normalize_date($_POST['sales_date']);
if (!$date) {
echo json_encode(["status" => "error", "message" => "실적일(sales_date) 형식 오류"]);
exit;
}
$_POST['sales_date'] = $date;
}
/* --------------------------------------------------
🔥 quantity / unit_price / discount 변경 여부 확인
----------------------------------------------------- */
$qtyChanged = array_key_exists('quantity', $_POST);
$unitChanged = array_key_exists('unit_price', $_POST);
$discountChanged = array_key_exists('discount', $_POST);
if ($qtyChanged || $unitChanged || $discountChanged) {
// 기존 값 가져오기
$old = $pdo->prepare("
SELECT quantity, unit_price, discount
FROM sales_results
WHERE seq_no = ?
");
$old->execute([$seq_no]);
$oldData = $old->fetch();
// 새 값이 있으면 새 값 사용, 없으면 기존 값 사용
$qty = isset($_POST['quantity']) ? (int)$_POST['quantity'] : (int)$oldData['quantity'];
$unit = isset($_POST['unit_price']) ? (int)$_POST['unit_price'] : (int)$oldData['unit_price'];
$discount = isset($_POST['discount']) ? (int)$_POST['discount'] : (int)$oldData['discount'];
// 서버에서 총금액 재계산
$total_amount = ($qty * $unit) - $discount;
if ($total_amount < 0) $total_amount = 0;
$_POST['total_amount'] = $total_amount; // 🔥 강제 반영
}
/* -----------------------------
🔵 Partial Update (빈값은 무시)
----------------------------- */
$fields = [];
$params = [];
foreach ($_POST as $key => $val) {
// NULL, 빈문자, undefined는 UPDATE 안함
if ($val === '' || $val === null || $val === 'undefined') {
continue;
}
$fields[] = "$key = ?";
$params[] = $val;
}
if (!empty($fields)) {
$sql = "UPDATE sales_results SET "
. implode(", ", $fields)
. ", updated_at = NOW()
WHERE seq_no = ?";
$params[] = $seq_no;
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
}
echo json_encode(["status" => "ok"]);
exit;
}
/* =====================================================
4) DELETE
===================================================== */
if ($action === "delete") {
$stmt = $pdo->prepare("DELETE FROM sales_results WHERE seq_no = ?");
$stmt->execute([$_POST['seq_no']]);
echo json_encode(["status" => "ok"]);
exit;
}
/* =====================================================
요청 없음
===================================================== */
echo json_encode(["status" => "fail", "message" => "잘못된 요청"]);
exit;
?>