Initial project import for team collaboration.
Exclude local docs, MCP, and secrets via gitignore. Made-with: Cursor
This commit is contained in:
0
app/Helpers/.gitkeep
Normal file
0
app/Helpers/.gitkeep
Normal file
158
app/Helpers/admin_helper.php
Normal file
158
app/Helpers/admin_helper.php
Normal file
@@ -0,0 +1,158 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Config\Roles;
|
||||
|
||||
if (! function_exists('admin_effective_lg_idx')) {
|
||||
/**
|
||||
* 현재 로그인한 관리자가 작업 대상으로 사용하는 지자체 PK.
|
||||
* Super admin → admin_selected_lg_idx, 지자체 관리자 → mb_lg_idx, 그 외 null.
|
||||
*/
|
||||
function admin_effective_lg_idx(): ?int
|
||||
{
|
||||
$level = (int) session()->get('mb_level');
|
||||
if ($level === Roles::LEVEL_SUPER_ADMIN) {
|
||||
$idx = session()->get('admin_selected_lg_idx');
|
||||
return $idx !== null && $idx !== '' ? (int) $idx : null;
|
||||
}
|
||||
if ($level === Roles::LEVEL_LOCAL_ADMIN) {
|
||||
$idx = session()->get('mb_lg_idx');
|
||||
return $idx !== null && $idx !== '' ? (int) $idx : null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('get_admin_nav_items')) {
|
||||
/**
|
||||
* 관리자 상단 메뉴 항목 (DB menu 테이블, admin 타입, 현재 지자체·mb_level 기준, 평면 배열).
|
||||
* 지자체 미선택(super admin)이면 빈 배열. 테이블/조회 실패 시에도 빈 배열.
|
||||
*
|
||||
* 하위 메뉴 포함 트리 구조가 필요하면 get_admin_nav_tree() 사용.
|
||||
*/
|
||||
function get_admin_nav_items(): array
|
||||
{
|
||||
try {
|
||||
$lgIdx = admin_effective_lg_idx();
|
||||
if ($lgIdx === null) {
|
||||
return [];
|
||||
}
|
||||
$typeRow = model(\App\Models\MenuTypeModel::class)->getByCode('admin');
|
||||
if (! $typeRow) {
|
||||
return [];
|
||||
}
|
||||
$mbLevel = (int) session()->get('mb_level');
|
||||
return model(\App\Models\MenuModel::class)->getVisibleByLevel((int) $typeRow->mt_idx, $mbLevel, $lgIdx);
|
||||
} catch (\Throwable $e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('build_menu_tree')) {
|
||||
/**
|
||||
* menu 평면 배열을 mm_pidx/mm_idx 기준 트리로 변환.
|
||||
*
|
||||
* @param array<int,object> $items
|
||||
* @return array<int,object> 루트 노드 배열
|
||||
*/
|
||||
function build_menu_tree(array $items): array
|
||||
{
|
||||
$map = [];
|
||||
foreach ($items as $item) {
|
||||
$item->children = [];
|
||||
$map[(int) $item->mm_idx] = $item;
|
||||
}
|
||||
$roots = [];
|
||||
foreach ($map as $id => $item) {
|
||||
$pidx = (int) $item->mm_pidx;
|
||||
if ($pidx === 0 || ! isset($map[$pidx])) {
|
||||
$roots[] = $item;
|
||||
} else {
|
||||
$map[$pidx]->children[] = $item;
|
||||
}
|
||||
}
|
||||
return $roots;
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('flatten_menu_tree')) {
|
||||
/**
|
||||
* 트리 구조의 메뉴를 상위 → 하위 순으로 평면 배열로 풀어낸다.
|
||||
* 관리자 메뉴 목록 화면에서 "부모 바로 아래에 자식"이 나오도록 하기 위한 용도.
|
||||
*
|
||||
* @param array<int,object> $tree
|
||||
* @return array<int,object>
|
||||
*/
|
||||
function flatten_menu_tree(array $tree): array
|
||||
{
|
||||
$result = [];
|
||||
|
||||
$walk = function ($nodes) use (&$result, &$walk) {
|
||||
foreach ($nodes as $node) {
|
||||
$children = $node->children ?? [];
|
||||
// children 속성은 목록에서 사용하지 않으므로 제거
|
||||
unset($node->children);
|
||||
$result[] = $node;
|
||||
if (! empty($children)) {
|
||||
$walk($children);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$walk($tree);
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('get_admin_nav_tree')) {
|
||||
/**
|
||||
* 관리자 상단 메뉴 트리 (admin 타입, 현재 지자체·mb_level 기준).
|
||||
* 1차 메뉴는 mm_pidx=0, 하위 메뉴는 children 속성으로 접근.
|
||||
*/
|
||||
function get_admin_nav_tree(): array
|
||||
{
|
||||
$flat = get_admin_nav_items();
|
||||
if (empty($flat)) {
|
||||
return [];
|
||||
}
|
||||
return build_menu_tree($flat);
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('get_site_nav_tree')) {
|
||||
/**
|
||||
* 일반 사이트 상단 메뉴 트리 (site 타입, 현재 회원의 지자체·mb_level 기준).
|
||||
* 1차 메뉴는 mm_pidx=0, 하위 메뉴는 children 속성으로 접근.
|
||||
*/
|
||||
function get_site_nav_tree(): array
|
||||
{
|
||||
try {
|
||||
$lgIdx = session()->get('mb_lg_idx');
|
||||
// 시민 등 지자체 정보가 세션에 없으면 기본 지자체(1) 기준으로 메뉴를 보여 준다.
|
||||
if ($lgIdx === null || $lgIdx === '') {
|
||||
$lgIdx = 1;
|
||||
}
|
||||
$typeRow = model(\App\Models\MenuTypeModel::class)->getByCode('site');
|
||||
if (! $typeRow) {
|
||||
return [];
|
||||
}
|
||||
$mbLevel = (int) session()->get('mb_level');
|
||||
$menuModel = model(\App\Models\MenuModel::class);
|
||||
$flat = $menuModel->getVisibleByLevel((int) $typeRow->mt_idx, $mbLevel, (int) $lgIdx);
|
||||
// 현재 지자체에 site 메뉴가 없으면, 기본 지자체(1)의 메뉴를 한 번 복사한 뒤 다시 시도
|
||||
if (empty($flat)) {
|
||||
$menuModel->copyDefaultsFromLg((int) $typeRow->mt_idx, 1, (int) $lgIdx);
|
||||
$flat = $menuModel->getVisibleByLevel((int) $typeRow->mt_idx, $mbLevel, (int) $lgIdx);
|
||||
}
|
||||
if (empty($flat)) {
|
||||
return [];
|
||||
}
|
||||
return build_menu_tree($flat);
|
||||
} catch (\Throwable $e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
62
app/Helpers/pii_encryption_helper.php
Normal file
62
app/Helpers/pii_encryption_helper.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* PII(개인정보) 필드 암호화/복호화 헬퍼.
|
||||
* encryption.key 가 .env에 설정된 경우에만 동작. 키가 없으면 평문 유지(기존 데이터 호환).
|
||||
*
|
||||
* 저장 형식: 암호화된 값은 "ENC:" + base64(암호문) 으로 저장. "ENC:" 없으면 평문(기존)으로 간주.
|
||||
*/
|
||||
if (! function_exists('pii_encrypt')) {
|
||||
function pii_encrypt(?string $value): string
|
||||
{
|
||||
if ($value === null || $value === '') {
|
||||
return '';
|
||||
}
|
||||
try {
|
||||
$config = config('Encryption');
|
||||
if ($config->key === '') {
|
||||
return $value;
|
||||
}
|
||||
$encrypter = service('encrypter');
|
||||
$encrypted = $encrypter->encrypt($value);
|
||||
|
||||
return 'ENC:' . base64_encode($encrypted);
|
||||
} catch (Throwable) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('pii_decrypt')) {
|
||||
function pii_decrypt(?string $value): string
|
||||
{
|
||||
if ($value === null || $value === '') {
|
||||
return '';
|
||||
}
|
||||
if (strpos($value, 'ENC:') !== 0) {
|
||||
return $value;
|
||||
}
|
||||
try {
|
||||
$config = config('Encryption');
|
||||
if ($config->key === '') {
|
||||
return $value;
|
||||
}
|
||||
$encrypter = service('encrypter');
|
||||
$raw = base64_decode(substr($value, 4), true);
|
||||
if ($raw === false) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return $encrypter->decrypt($raw);
|
||||
} catch (Throwable) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 암호화 대상 개인정보 필드 (member 테이블) */
|
||||
if (! defined('PII_ENCRYPTED_FIELDS')) {
|
||||
define('PII_ENCRYPTED_FIELDS', ['mb_phone', 'mb_email']);
|
||||
}
|
||||
Reference in New Issue
Block a user