From 45f73ce7c82f524942e436905a87cea18210f326 Mon Sep 17 00:00:00 2001 From: pro Date: Thu, 22 Jan 2026 16:12:55 +0900 Subject: [PATCH] =?UTF-8?q?feat:=EB=B0=94=EB=A1=9C=EB=B9=8C=20=ED=85=8C?= =?UTF-8?q?=EB=84=8C=ED=8A=B8(=ED=9A=8C=EC=9B=90=EC=82=AC)=20=EB=8F=99?= =?UTF-8?q?=EA=B8=B0=ED=99=94=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - barobill_companies 테이블에서 barobill_members로 동기화 API 구현 - 바로빌본사 설정 페이지에 테넌트 목록 및 동기화 버튼 추가 - 동기화 시 신규 데이터 생성 및 기존 데이터 업데이트 --- .../Barobill/BarobillConfigController.php | 317 ++++++++++ .../views/barobill/config/index.blade.php | 560 ++++++++++++++++++ routes/api.php | 48 ++ 3 files changed, 925 insertions(+) create mode 100644 app/Http/Controllers/Api/Admin/Barobill/BarobillConfigController.php create mode 100644 resources/views/barobill/config/index.blade.php diff --git a/app/Http/Controllers/Api/Admin/Barobill/BarobillConfigController.php b/app/Http/Controllers/Api/Admin/Barobill/BarobillConfigController.php new file mode 100644 index 00000000..c19ac817 --- /dev/null +++ b/app/Http/Controllers/Api/Admin/Barobill/BarobillConfigController.php @@ -0,0 +1,317 @@ +orderBy('environment') + ->orderBy('is_active', 'desc') + ->orderBy('created_at', 'desc') + ->get(); + + // HTMX 요청 시 HTML 반환 + if ($request->header('HX-Request')) { + return response( + view('barobill.config.partials.table', compact('configs'))->render(), + 200, + ['Content-Type' => 'text/html'] + ); + } + + return response()->json([ + 'success' => true, + 'data' => $configs, + ]); + } + + /** + * 설정 등록 + */ + public function store(Request $request): JsonResponse + { + $validated = $request->validate([ + 'name' => 'required|string|max:50', + 'environment' => 'required|in:test,production', + 'cert_key' => 'required|string|max:100', + 'corp_num' => 'nullable|string|max:20', + 'base_url' => 'required|url|max:255', + 'description' => 'nullable|string|max:500', + 'is_active' => 'nullable|boolean', + ], [ + 'name.required' => '설정 이름을 입력해주세요.', + 'environment.required' => '환경을 선택해주세요.', + 'cert_key.required' => '인증키(CERTKEY)를 입력해주세요.', + 'base_url.required' => '서버 URL을 입력해주세요.', + 'base_url.url' => '올바른 URL 형식을 입력해주세요.', + ]); + + DB::beginTransaction(); + try { + // is_active가 true이면 같은 환경의 다른 설정들은 비활성화 + if ($validated['is_active'] ?? false) { + BarobillConfig::where('environment', $validated['environment']) + ->update(['is_active' => false]); + } + + $config = BarobillConfig::create($validated); + + DB::commit(); + + return response()->json([ + 'success' => true, + 'message' => '바로빌 설정이 등록되었습니다.', + 'data' => $config, + ], 201); + } catch (\Exception $e) { + DB::rollBack(); + Log::error('바로빌 설정 등록 실패', ['error' => $e->getMessage()]); + + return response()->json([ + 'success' => false, + 'message' => '설정 등록 중 오류가 발생했습니다.', + ], 500); + } + } + + /** + * 설정 상세 조회 + */ + public function show(int $id): JsonResponse + { + $config = BarobillConfig::find($id); + + if (!$config) { + return response()->json([ + 'success' => false, + 'message' => '설정을 찾을 수 없습니다.', + ], 404); + } + + return response()->json([ + 'success' => true, + 'data' => $config, + ]); + } + + /** + * 설정 수정 + */ + public function update(Request $request, int $id): JsonResponse + { + $config = BarobillConfig::find($id); + + if (!$config) { + return response()->json([ + 'success' => false, + 'message' => '설정을 찾을 수 없습니다.', + ], 404); + } + + $validated = $request->validate([ + 'name' => 'required|string|max:50', + 'environment' => 'required|in:test,production', + 'cert_key' => 'required|string|max:100', + 'corp_num' => 'nullable|string|max:20', + 'base_url' => 'required|url|max:255', + 'description' => 'nullable|string|max:500', + 'is_active' => 'nullable|boolean', + ]); + + DB::beginTransaction(); + try { + // is_active가 true이면 같은 환경의 다른 설정들은 비활성화 + if ($validated['is_active'] ?? false) { + BarobillConfig::where('environment', $validated['environment']) + ->where('id', '!=', $id) + ->update(['is_active' => false]); + } + + $config->update($validated); + + DB::commit(); + + return response()->json([ + 'success' => true, + 'message' => '바로빌 설정이 수정되었습니다.', + 'data' => $config->fresh(), + ]); + } catch (\Exception $e) { + DB::rollBack(); + Log::error('바로빌 설정 수정 실패', ['error' => $e->getMessage()]); + + return response()->json([ + 'success' => false, + 'message' => '설정 수정 중 오류가 발생했습니다.', + ], 500); + } + } + + /** + * 설정 삭제 + */ + public function destroy(int $id): JsonResponse + { + $config = BarobillConfig::find($id); + + if (!$config) { + return response()->json([ + 'success' => false, + 'message' => '설정을 찾을 수 없습니다.', + ], 404); + } + + if ($config->is_active) { + return response()->json([ + 'success' => false, + 'message' => '활성화된 설정은 삭제할 수 없습니다. 먼저 비활성화해주세요.', + ], 422); + } + + $config->delete(); + + return response()->json([ + 'success' => true, + 'message' => '바로빌 설정이 삭제되었습니다.', + ]); + } + + /** + * 설정 활성화/비활성화 토글 + */ + public function toggleActive(int $id): JsonResponse + { + $config = BarobillConfig::find($id); + + if (!$config) { + return response()->json([ + 'success' => false, + 'message' => '설정을 찾을 수 없습니다.', + ], 404); + } + + DB::beginTransaction(); + try { + if (!$config->is_active) { + // 활성화하려면 같은 환경의 다른 설정들 비활성화 + BarobillConfig::where('environment', $config->environment) + ->where('id', '!=', $id) + ->update(['is_active' => false]); + } + + $config->update(['is_active' => !$config->is_active]); + + DB::commit(); + + return response()->json([ + 'success' => true, + 'message' => $config->is_active ? '설정이 활성화되었습니다.' : '설정이 비활성화되었습니다.', + 'data' => $config->fresh(), + ]); + } catch (\Exception $e) { + DB::rollBack(); + Log::error('바로빌 설정 토글 실패', ['error' => $e->getMessage()]); + + return response()->json([ + 'success' => false, + 'message' => '설정 변경 중 오류가 발생했습니다.', + ], 500); + } + } + + /** + * barobill_companies에서 barobill_members로 동기화 + */ + public function syncCompanies(): JsonResponse + { + DB::beginTransaction(); + try { + // barobill_companies 테이블에서 데이터 조회 + $companies = DB::table('barobill_companies') + ->where('is_active', 1) + ->whereNotNull('barobill_user_id') + ->get(); + + $synced = 0; + $skipped = 0; + $errors = []; + + foreach ($companies as $company) { + // 이미 존재하는지 확인 (사업자번호 기준) + $existing = BarobillMember::where('biz_no', $company->corp_num)->first(); + + if ($existing) { + // 기존 데이터 업데이트 (비밀번호 제외) + $existing->update([ + 'corp_name' => $company->company_name, + 'ceo_name' => $company->ceo_name ?? $existing->ceo_name, + 'barobill_id' => $company->barobill_user_id, + ]); + $skipped++; + } else { + // 새로 생성 + BarobillMember::create([ + 'tenant_id' => 1, // 기본 테넌트 + 'biz_no' => $company->corp_num, + 'corp_name' => $company->company_name, + 'ceo_name' => $company->ceo_name ?? '', + 'barobill_id' => $company->barobill_user_id, + 'barobill_pwd' => '', // 비밀번호는 별도로 입력 필요 + 'status' => 'active', + ]); + $synced++; + } + } + + DB::commit(); + + return response()->json([ + 'success' => true, + 'message' => "동기화 완료: 신규 {$synced}건, 업데이트 {$skipped}건", + 'data' => [ + 'synced' => $synced, + 'updated' => $skipped, + 'total' => $companies->count(), + ], + ]); + } catch (\Exception $e) { + DB::rollBack(); + Log::error('바로빌 회원사 동기화 실패', ['error' => $e->getMessage()]); + + return response()->json([ + 'success' => false, + 'message' => '동기화 중 오류가 발생했습니다: ' . $e->getMessage(), + ], 500); + } + } + + /** + * barobill_companies 목록 조회 + */ + public function getCompanies(): JsonResponse + { + $companies = DB::table('barobill_companies') + ->select('id', 'company_name', 'corp_num', 'barobill_user_id', 'ceo_name', 'is_active', 'memo', 'created_at') + ->orderBy('id') + ->get(); + + return response()->json([ + 'success' => true, + 'data' => $companies, + ]); + } +} diff --git a/resources/views/barobill/config/index.blade.php b/resources/views/barobill/config/index.blade.php new file mode 100644 index 00000000..c1464566 --- /dev/null +++ b/resources/views/barobill/config/index.blade.php @@ -0,0 +1,560 @@ +@extends('layouts.app') + +@section('title', '바로빌설정') + +@section('content') + +
+
+

바로빌설정

+

바로빌 API 연동을 위한 인증키 및 서버 설정을 관리합니다

+
+ +
+ + +
+
+ + + +
+

바로빌 연동 안내

+
    +
  • 테스트서버와 운영서버 각각 별도의 인증키(CERTKEY)가 필요합니다.
  • +
  • 인증키는 바로빌 개발자센터에서 발급받을 수 있습니다.
  • +
  • 환경당 하나의 설정만 활성화할 수 있습니다.
  • +
+
+
+
+ + +
+ +
+
+
+
+ + + +
+
+

테스트서버

+

https://testws.baroservice.com

+
+
+ + 미설정 + +
+
+
+ + + + +

로딩 중...

+
+
+
+ + +
+
+
+
+ + + +
+
+

운영서버

+

https://ws.baroservice.com

+
+
+ + 미설정 + +
+
+
+ + + + +

로딩 중...

+
+
+
+
+ + +
+
+
+
+ + + +
+
+

테넌트(회원사) 동기화

+

sales 시스템의 테넌트 데이터를 회원사로 동기화합니다

+
+
+ +
+
+
+
+

테넌트 목록을 불러오는 중...

+
+
+
+
+ + +
+
+

전체 설정 목록

+
+
+
+
+
+
+
+ + +@include('barobill.config.partials.modal-form') +@endsection + +@push('scripts') + +@endpush diff --git a/routes/api.php b/routes/api.php index 241263ba..5f661e8f 100644 --- a/routes/api.php +++ b/routes/api.php @@ -84,10 +84,28 @@ Route::patch('/{id}/status', [\App\Http\Controllers\Api\Admin\FundScheduleController::class, 'updateStatus'])->name('status'); }); + // 바로빌 설정 API + Route::prefix('barobill/configs')->name('barobill.configs.')->group(function () { + Route::get('/', [\App\Http\Controllers\Api\Admin\Barobill\BarobillConfigController::class, 'index'])->name('index'); + Route::post('/', [\App\Http\Controllers\Api\Admin\Barobill\BarobillConfigController::class, 'store'])->name('store'); + Route::get('/{id}', [\App\Http\Controllers\Api\Admin\Barobill\BarobillConfigController::class, 'show'])->name('show'); + Route::put('/{id}', [\App\Http\Controllers\Api\Admin\Barobill\BarobillConfigController::class, 'update'])->name('update'); + Route::delete('/{id}', [\App\Http\Controllers\Api\Admin\Barobill\BarobillConfigController::class, 'destroy'])->name('destroy'); + Route::post('/{id}/toggle-active', [\App\Http\Controllers\Api\Admin\Barobill\BarobillConfigController::class, 'toggleActive'])->name('toggle-active'); + }); + + // 바로빌 테넌트(회원사) 동기화 API + Route::prefix('barobill/companies')->name('barobill.companies.')->group(function () { + Route::get('/', [\App\Http\Controllers\Api\Admin\Barobill\BarobillConfigController::class, 'getCompanies'])->name('index'); + Route::post('/sync', [\App\Http\Controllers\Api\Admin\Barobill\BarobillConfigController::class, 'syncCompanies'])->name('sync'); + }); + // 바로빌 회원사 관리 API Route::prefix('barobill/members')->name('barobill.members.')->group(function () { // 통계 Route::get('/stats', [\App\Http\Controllers\Api\Admin\Barobill\BarobillMemberController::class, 'stats'])->name('stats'); + // 서비스 코드 목록 (카드사/은행) + Route::get('/service-codes', [\App\Http\Controllers\Api\Admin\Barobill\BarobillMemberController::class, 'getServiceCodes'])->name('service-codes'); // 기본 CRUD Route::get('/', [\App\Http\Controllers\Api\Admin\Barobill\BarobillMemberController::class, 'index'])->name('index'); @@ -95,6 +113,36 @@ Route::get('/{id}', [\App\Http\Controllers\Api\Admin\Barobill\BarobillMemberController::class, 'show'])->name('show'); Route::put('/{id}', [\App\Http\Controllers\Api\Admin\Barobill\BarobillMemberController::class, 'update'])->name('update'); Route::delete('/{id}', [\App\Http\Controllers\Api\Admin\Barobill\BarobillMemberController::class, 'destroy'])->name('destroy'); + + // ========================================== + // 계좌 관련 URL 조회 + // ========================================== + Route::post('/{id}/bank-account-url', [\App\Http\Controllers\Api\Admin\Barobill\BarobillMemberController::class, 'getBankAccountUrl'])->name('bank-account-url'); + Route::post('/{id}/bank-account-manage-url', [\App\Http\Controllers\Api\Admin\Barobill\BarobillMemberController::class, 'getBankAccountManageUrl'])->name('bank-account-manage-url'); + Route::post('/{id}/bank-account-log-url', [\App\Http\Controllers\Api\Admin\Barobill\BarobillMemberController::class, 'getBankAccountLogUrl'])->name('bank-account-log-url'); + Route::get('/{id}/bank-accounts', [\App\Http\Controllers\Api\Admin\Barobill\BarobillMemberController::class, 'getBankAccounts'])->name('bank-accounts'); + + // ========================================== + // 카드 관련 URL 조회 + // ========================================== + Route::post('/{id}/card-url', [\App\Http\Controllers\Api\Admin\Barobill\BarobillMemberController::class, 'getCardUrl'])->name('card-url'); + Route::post('/{id}/card-manage-url', [\App\Http\Controllers\Api\Admin\Barobill\BarobillMemberController::class, 'getCardManageUrl'])->name('card-manage-url'); + Route::post('/{id}/card-log-url', [\App\Http\Controllers\Api\Admin\Barobill\BarobillMemberController::class, 'getCardLogUrl'])->name('card-log-url'); + Route::get('/{id}/cards', [\App\Http\Controllers\Api\Admin\Barobill\BarobillMemberController::class, 'getCards'])->name('cards'); + + // ========================================== + // 전자세금계산서 관련 URL 조회 + // ========================================== + Route::post('/{id}/certificate-url', [\App\Http\Controllers\Api\Admin\Barobill\BarobillMemberController::class, 'getCertificateUrl'])->name('certificate-url'); + Route::post('/{id}/tax-invoice-url', [\App\Http\Controllers\Api\Admin\Barobill\BarobillMemberController::class, 'getTaxInvoiceUrl'])->name('tax-invoice-url'); + Route::post('/{id}/tax-invoice-list-url', [\App\Http\Controllers\Api\Admin\Barobill\BarobillMemberController::class, 'getTaxInvoiceListUrl'])->name('tax-invoice-list-url'); + + // ========================================== + // 공통 조회 + // ========================================== + Route::post('/{id}/cash-charge-url', [\App\Http\Controllers\Api\Admin\Barobill\BarobillMemberController::class, 'getCashChargeUrl'])->name('cash-charge-url'); + Route::get('/{id}/certificate-status', [\App\Http\Controllers\Api\Admin\Barobill\BarobillMemberController::class, 'getCertificateStatus'])->name('certificate-status'); + Route::get('/{id}/balance', [\App\Http\Controllers\Api\Admin\Barobill\BarobillMemberController::class, 'getBalance'])->name('balance'); }); // 테넌트 관리 API