diff --git a/LOGICAL_RELATIONSHIPS.md b/LOGICAL_RELATIONSHIPS.md index 41a4318..22f2a07 100644 --- a/LOGICAL_RELATIONSHIPS.md +++ b/LOGICAL_RELATIONSHIPS.md @@ -1,6 +1,6 @@ # 논리적 데이터베이스 관계 문서 -> **자동 생성**: 2025-12-17 23:09:53 +> **자동 생성**: 2025-12-18 11:37:56 > **소스**: Eloquent 모델 관계 분석 ## 📊 모델별 관계 현황 @@ -306,6 +306,14 @@ ### price_revisions - **price()**: belongsTo → `prices` +### push_device_tokens +**모델**: `App\Models\PushDeviceToken` + + +### push_notification_settings +**모델**: `App\Models\PushNotificationSetting` + + ### lots **모델**: `App\Models\Qualitys\Lot` @@ -339,6 +347,36 @@ ### quote_revisions - **quote()**: belongsTo → `quotes` - **reviser()**: belongsTo → `users` +### approvals +**모델**: `App\Models\Tenants\Approval` + +- **form()**: belongsTo → `approval_forms` +- **drafter()**: belongsTo → `users` +- **creator()**: belongsTo → `users` +- **updater()**: belongsTo → `users` +- **steps()**: hasMany → `approval_steps` +- **approverSteps()**: hasMany → `approval_steps` +- **referenceSteps()**: hasMany → `approval_steps` + +### approval_forms +**모델**: `App\Models\Tenants\ApprovalForm` + +- **creator()**: belongsTo → `users` +- **updater()**: belongsTo → `users` +- **approvals()**: hasMany → `approvals` + +### approval_lines +**모델**: `App\Models\Tenants\ApprovalLine` + +- **creator()**: belongsTo → `users` +- **updater()**: belongsTo → `users` + +### approval_steps +**모델**: `App\Models\Tenants\ApprovalStep` + +- **approval()**: belongsTo → `approvals` +- **approver()**: belongsTo → `users` + ### attendances **모델**: `App\Models\Tenants\Attendance` @@ -392,6 +430,20 @@ ### payments - **subscription()**: belongsTo → `subscriptions` +### payrolls +**모델**: `App\Models\Tenants\Payroll` + +- **user()**: belongsTo → `users` +- **confirmer()**: belongsTo → `users` +- **withdrawal()**: belongsTo → `withdrawals` +- **creator()**: belongsTo → `users` +- **updater()**: belongsTo → `users` + +### payroll_settings +**모델**: `App\Models\Tenants\PayrollSetting` + +- **tenant()**: belongsTo → `tenants` + ### department_users **모델**: `App\Models\Tenants\Pivots\DepartmentUser` diff --git a/app/Exceptions/DuplicateCodeException.php b/app/Exceptions/DuplicateCodeException.php index 4c4e8f8..e00c228 100644 --- a/app/Exceptions/DuplicateCodeException.php +++ b/app/Exceptions/DuplicateCodeException.php @@ -48,4 +48,4 @@ public function render() 'duplicate_code' => $this->duplicateCode, ], 400); } -} \ No newline at end of file +} diff --git a/app/Http/Controllers/Api/V1/PushNotificationController.php b/app/Http/Controllers/Api/V1/PushNotificationController.php index a3e911c..715c560 100644 --- a/app/Http/Controllers/Api/V1/PushNotificationController.php +++ b/app/Http/Controllers/Api/V1/PushNotificationController.php @@ -88,4 +88,4 @@ public function getNotificationTypes() ]; }); } -} \ No newline at end of file +} diff --git a/app/Http/Middleware/LogApiRequest.php b/app/Http/Middleware/LogApiRequest.php index ab4841b..82d9101 100644 --- a/app/Http/Middleware/LogApiRequest.php +++ b/app/Http/Middleware/LogApiRequest.php @@ -69,6 +69,7 @@ protected function shouldSkip(Request $request): bool return true; } } + return false; } @@ -124,6 +125,7 @@ protected function maskSensitiveHeaders(array $headers): array $headers[$header] = ['***MASKED***']; } } + return $headers; } @@ -136,6 +138,7 @@ protected function maskSensitiveFields(array $data): array $data[$key] = $this->maskSensitiveFields($value); } } + return $data; } @@ -148,7 +151,7 @@ protected function truncateResponse(?string $content): ?string // 10KB 이상이면 잘라내기 $maxLength = 10 * 1024; if (strlen($content) > $maxLength) { - return substr($content, 0, $maxLength) . '...[TRUNCATED]'; + return substr($content, 0, $maxLength).'...[TRUNCATED]'; } return $content; @@ -157,7 +160,7 @@ protected function truncateResponse(?string $content): ?string protected function writeToLogFile(array $logData): void { $logMessage = sprintf( - "[%s] %s %s | Status: %d | Duration: %dms | IP: %s | User: %s", + '[%s] %s %s | Status: %d | Duration: %dms | IP: %s | User: %s', now()->format('Y-m-d H:i:s'), $logData['method'], $logData['url'], @@ -188,13 +191,14 @@ protected function getOrCreateGroupId(?int $tenantId, ?int $userId): string if ($existingGroupId) { // TTL 갱신 (연속 요청 시 그룹 유지) cache()->put($cacheKey, $existingGroupId, $groupTtl); + return $existingGroupId; } // 새 그룹 ID 생성 - $newGroupId = now()->format('Ymd_His_') . substr(md5(uniqid((string) mt_rand(), true)), 0, 8); + $newGroupId = now()->format('Ymd_His_').substr(md5(uniqid((string) mt_rand(), true)), 0, 8); cache()->put($cacheKey, $newGroupId, $groupTtl); return $newGroupId; } -} \ No newline at end of file +} diff --git a/app/Http/Requests/Push/RegisterTokenRequest.php b/app/Http/Requests/Push/RegisterTokenRequest.php index 5f71378..55aa1b7 100644 --- a/app/Http/Requests/Push/RegisterTokenRequest.php +++ b/app/Http/Requests/Push/RegisterTokenRequest.php @@ -40,4 +40,4 @@ public function messages(): array 'platform.in' => __('error.push.platform_invalid'), ]; } -} \ No newline at end of file +} diff --git a/app/Models/ApiRequestLog.php b/app/Models/ApiRequestLog.php index 931e91f..9ecdaf7 100644 --- a/app/Models/ApiRequestLog.php +++ b/app/Models/ApiRequestLog.php @@ -63,4 +63,4 @@ public static function pruneOldLogs(): int { return static::where('created_at', '<', now()->subDay())->delete(); } -} \ No newline at end of file +} diff --git a/app/Models/Items/ItemDetail.php b/app/Models/Items/ItemDetail.php index 03f6497..2cd6d08 100644 --- a/app/Models/Items/ItemDetail.php +++ b/app/Models/Items/ItemDetail.php @@ -111,4 +111,4 @@ public function requiresInspection(): bool { return $this->is_inspection === 'Y'; } -} \ No newline at end of file +} diff --git a/app/Models/PushDeviceToken.php b/app/Models/PushDeviceToken.php index 764bb5d..fd87ee5 100644 --- a/app/Models/PushDeviceToken.php +++ b/app/Models/PushDeviceToken.php @@ -76,4 +76,4 @@ public function scopeForUser($query, int $userId) { return $query->where('user_id', $userId); } -} \ No newline at end of file +} diff --git a/app/Models/PushNotificationSetting.php b/app/Models/PushNotificationSetting.php index 7dbdd21..6cbf0df 100644 --- a/app/Models/PushNotificationSetting.php +++ b/app/Models/PushNotificationSetting.php @@ -128,4 +128,4 @@ public function scopeOfType($query, string $type) { return $query->where('notification_type', $type); } -} \ No newline at end of file +} diff --git a/app/Services/PushNotificationService.php b/app/Services/PushNotificationService.php index 5c9ae78..47b451e 100644 --- a/app/Services/PushNotificationService.php +++ b/app/Services/PushNotificationService.php @@ -274,4 +274,4 @@ protected function getDefaultSound(string $type): string default => PushNotificationSetting::SOUND_DEFAULT, }; } -} \ No newline at end of file +} diff --git a/app/Swagger/v1/PushApi.php b/app/Swagger/v1/PushApi.php index a162055..2985212 100644 --- a/app/Swagger/v1/PushApi.php +++ b/app/Swagger/v1/PushApi.php @@ -16,6 +16,7 @@ * schema="PushDeviceToken", * type="object", * description="푸시 디바이스 토큰", + * * @OA\Property(property="id", type="integer", example=1), * @OA\Property(property="tenant_id", type="integer", example=1), * @OA\Property(property="user_id", type="integer", example=5), @@ -33,6 +34,7 @@ * schema="PushNotificationSetting", * type="object", * description="푸시 알림 설정", + * * @OA\Property(property="id", type="integer", example=1), * @OA\Property(property="tenant_id", type="integer", example=1), * @OA\Property(property="user_id", type="integer", example=5), @@ -49,6 +51,7 @@ * schema="RegisterTokenRequest", * type="object", * required={"token", "platform"}, + * * @OA\Property(property="token", type="string", minLength=10, example="dGhpcyBpcyBhIHNhbXBsZSBGQ00gdG9rZW4...", description="FCM 토큰"), * @OA\Property(property="platform", type="string", enum={"ios", "android", "web"}, example="android", description="디바이스 플랫폼"), * @OA\Property(property="device_name", type="string", nullable=true, maxLength=255, example="Samsung Galaxy S24", description="디바이스명"), @@ -59,6 +62,7 @@ * schema="UnregisterTokenRequest", * type="object", * required={"token"}, + * * @OA\Property(property="token", type="string", example="dGhpcyBpcyBhIHNhbXBsZSBGQ00gdG9rZW4...", description="해제할 FCM 토큰") * ) * @@ -66,13 +70,16 @@ * schema="UpdatePushSettingsRequest", * type="object", * required={"settings"}, + * * @OA\Property( * property="settings", * type="array", * description="알림 설정 배열", + * * @OA\Items( * type="object", * required={"notification_type", "is_enabled"}, + * * @OA\Property(property="notification_type", type="string", enum={"deposit", "withdrawal", "order", "approval", "attendance", "notice", "system"}, example="deposit"), * @OA\Property(property="is_enabled", type="boolean", example=true), * @OA\Property(property="sound", type="string", nullable=true, enum={"default.wav", "deposit.wav", "withdrawal.wav", "order.wav", "approval.wav", "urgent.wav"}, example="deposit.wav"), @@ -85,16 +92,20 @@ * @OA\Schema( * schema="NotificationTypesResponse", * type="object", + * * @OA\Property( * property="types", * type="array", * description="지원하는 알림 유형 목록", + * * @OA\Items(type="string", example="deposit") * ), + * * @OA\Property( * property="sounds", * type="array", * description="지원하는 알림음 목록", + * * @OA\Items(type="string", example="deposit.wav") * ) * ) @@ -111,16 +122,20 @@ class PushApi * * @OA\RequestBody( * required=true, + * * @OA\JsonContent(ref="#/components/schemas/RegisterTokenRequest") * ), * * @OA\Response( * response=200, * description="토큰 등록 성공", + * * @OA\JsonContent( * allOf={ + * * @OA\Schema(ref="#/components/schemas/ApiResponse"), * @OA\Schema( + * * @OA\Property(property="message", type="string", example="푸시 토큰이 등록되었습니다."), * @OA\Property(property="data", ref="#/components/schemas/PushDeviceToken") * ) @@ -144,16 +159,20 @@ public function registerToken() {} * * @OA\RequestBody( * required=true, + * * @OA\JsonContent(ref="#/components/schemas/UnregisterTokenRequest") * ), * * @OA\Response( * response=200, * description="토큰 해제 성공", + * * @OA\JsonContent( * allOf={ + * * @OA\Schema(ref="#/components/schemas/ApiResponse"), * @OA\Schema( + * * @OA\Property(property="message", type="string", example="푸시 토큰이 해제되었습니다."), * @OA\Property( * property="data", @@ -182,13 +201,17 @@ public function unregisterToken() {} * @OA\Response( * response=200, * description="조회 성공", + * * @OA\JsonContent( * allOf={ + * * @OA\Schema(ref="#/components/schemas/ApiResponse"), * @OA\Schema( + * * @OA\Property( * property="data", * type="array", + * * @OA\Items(ref="#/components/schemas/PushDeviceToken") * ) * ) @@ -212,10 +235,13 @@ public function getTokens() {} * @OA\Response( * response=200, * description="조회 성공", + * * @OA\JsonContent( * allOf={ + * * @OA\Schema(ref="#/components/schemas/ApiResponse"), * @OA\Schema( + * * @OA\Property( * property="data", * type="object", @@ -248,20 +274,25 @@ public function getSettings() {} * * @OA\RequestBody( * required=true, + * * @OA\JsonContent(ref="#/components/schemas/UpdatePushSettingsRequest") * ), * * @OA\Response( * response=200, * description="설정 업데이트 성공", + * * @OA\JsonContent( * allOf={ + * * @OA\Schema(ref="#/components/schemas/ApiResponse"), * @OA\Schema( + * * @OA\Property(property="message", type="string", example="알림 설정이 업데이트되었습니다."), * @OA\Property( * property="data", * type="array", + * * @OA\Items(ref="#/components/schemas/PushNotificationSetting") * ) * ) @@ -286,10 +317,13 @@ public function updateSettings() {} * @OA\Response( * response=200, * description="조회 성공", + * * @OA\JsonContent( * allOf={ + * * @OA\Schema(ref="#/components/schemas/ApiResponse"), * @OA\Schema( + * * @OA\Property(property="data", ref="#/components/schemas/NotificationTypesResponse") * ) * } @@ -300,4 +334,4 @@ public function updateSettings() {} * ) */ public function getNotificationTypes() {} -} \ No newline at end of file +} diff --git a/database/migrations/2025_12_10_211539_add_options_column_to_products_table.php b/database/migrations/2025_12_10_211539_add_options_column_to_products_table.php index f34249f..4624173 100644 --- a/database/migrations/2025_12_10_211539_add_options_column_to_products_table.php +++ b/database/migrations/2025_12_10_211539_add_options_column_to_products_table.php @@ -26,4 +26,4 @@ public function down(): void $table->dropColumn('options'); }); } -}; \ No newline at end of file +}; diff --git a/database/migrations/2025_12_11_121758_modify_item_fields_unique_constraint.php b/database/migrations/2025_12_11_121758_modify_item_fields_unique_constraint.php index 35a2488..d3f983c 100644 --- a/database/migrations/2025_12_11_121758_modify_item_fields_unique_constraint.php +++ b/database/migrations/2025_12_11_121758_modify_item_fields_unique_constraint.php @@ -157,4 +157,4 @@ private function restoreFieldKeyPrefixes(): void ->where('field_key', 'note') ->update(['field_key' => 'material_receipts_note']); } -}; \ No newline at end of file +}; diff --git a/database/migrations/2025_12_11_204116_add_bom_column_to_products_table.php b/database/migrations/2025_12_11_204116_add_bom_column_to_products_table.php index 4f6fab8..1827147 100644 --- a/database/migrations/2025_12_11_204116_add_bom_column_to_products_table.php +++ b/database/migrations/2025_12_11_204116_add_bom_column_to_products_table.php @@ -25,4 +25,4 @@ public function down(): void $table->dropColumn('bom'); }); } -}; \ No newline at end of file +}; diff --git a/database/migrations/2025_12_11_220000_create_items_table.php b/database/migrations/2025_12_11_220000_create_items_table.php index f00bbce..02095d7 100644 --- a/database/migrations/2025_12_11_220000_create_items_table.php +++ b/database/migrations/2025_12_11_220000_create_items_table.php @@ -100,4 +100,4 @@ public function down(): void { Schema::dropIfExists('items'); } -}; \ No newline at end of file +}; diff --git a/database/migrations/2025_12_11_220100_create_item_id_mappings_table.php b/database/migrations/2025_12_11_220100_create_item_id_mappings_table.php index 7a6643d..f567527 100644 --- a/database/migrations/2025_12_11_220100_create_item_id_mappings_table.php +++ b/database/migrations/2025_12_11_220100_create_item_id_mappings_table.php @@ -38,4 +38,4 @@ public function down(): void { Schema::dropIfExists('item_id_mappings'); } -}; \ No newline at end of file +}; diff --git a/database/migrations/2025_12_11_220200_migrate_products_materials_to_items.php b/database/migrations/2025_12_11_220200_migrate_products_materials_to_items.php index 9d7501e..0ead93d 100644 --- a/database/migrations/2025_12_11_220200_migrate_products_materials_to_items.php +++ b/database/migrations/2025_12_11_220200_migrate_products_materials_to_items.php @@ -21,4 +21,4 @@ public function down(): void { // 비활성화 } -}; \ No newline at end of file +}; diff --git a/database/migrations/2025_12_12_100000_rollback_items_migration.php b/database/migrations/2025_12_12_100000_rollback_items_migration.php index a6c941d..f100257 100644 --- a/database/migrations/2025_12_12_100000_rollback_items_migration.php +++ b/database/migrations/2025_12_12_100000_rollback_items_migration.php @@ -1,7 +1,6 @@ whereNotIn('product_type', ['FG', 'PT']) ->count(); - Log::info("Phase 0: Normalizing item_types", [ + Log::info('Phase 0: Normalizing item_types', [ 'total_before' => $beforeCount, 'non_standard_count' => $nonStandardCount, ]); @@ -57,7 +57,7 @@ public function up(): void // 4. 삭제 후 건수 확인 $afterCount = DB::table('products')->count(); - Log::info("Phase 0: Normalization complete", [ + Log::info('Phase 0: Normalization complete', [ 'bom_deleted' => $bomDeleted, 'products_deleted' => $deleted, 'total_after' => $afterCount, @@ -71,6 +71,6 @@ public function up(): void */ public function down(): void { - Log::warning("Phase 0 rollback: Deleted data cannot be restored"); + Log::warning('Phase 0 rollback: Deleted data cannot be restored'); } -}; \ No newline at end of file +}; diff --git a/database/migrations/2025_12_13_152507_create_items_table.php b/database/migrations/2025_12_13_152507_create_items_table.php index b4ac5c4..7b1c538 100644 --- a/database/migrations/2025_12_13_152507_create_items_table.php +++ b/database/migrations/2025_12_13_152507_create_items_table.php @@ -68,4 +68,4 @@ public function down(): void { Schema::dropIfExists('items'); } -}; \ No newline at end of file +}; diff --git a/database/migrations/2025_12_13_152553_create_item_details_table.php b/database/migrations/2025_12_13_152553_create_item_details_table.php index 49bf04f..40eb7cc 100644 --- a/database/migrations/2025_12_13_152553_create_item_details_table.php +++ b/database/migrations/2025_12_13_152553_create_item_details_table.php @@ -67,4 +67,4 @@ public function down(): void { Schema::dropIfExists('item_details'); } -}; \ No newline at end of file +}; diff --git a/database/migrations/2025_12_13_152631_migrate_products_materials_to_items.php b/database/migrations/2025_12_13_152631_migrate_products_materials_to_items.php index a59ce2d..8807ec4 100644 --- a/database/migrations/2025_12_13_152631_migrate_products_materials_to_items.php +++ b/database/migrations/2025_12_13_152631_migrate_products_materials_to_items.php @@ -163,7 +163,7 @@ public function up(): void $materialCount++; } - Log::info("Phase 1.3: Data migration complete", [ + Log::info('Phase 1.3: Data migration complete', [ 'products_migrated' => $productCount, 'materials_migrated' => $materialCount, 'total' => $productCount + $materialCount, @@ -182,6 +182,6 @@ public function down(): void // ID 매핑 테이블 삭제 Schema::dropIfExists('item_id_mappings'); - Log::info("Phase 1.3: Data migration rolled back"); + Log::info('Phase 1.3: Data migration rolled back'); } -}; \ No newline at end of file +}; diff --git a/database/migrations/2025_12_13_160000_update_bom_child_item_ids_to_new_item_ids.php b/database/migrations/2025_12_13_160000_update_bom_child_item_ids_to_new_item_ids.php index e5c4ce1..f827533 100644 --- a/database/migrations/2025_12_13_160000_update_bom_child_item_ids_to_new_item_ids.php +++ b/database/migrations/2025_12_13_160000_update_bom_child_item_ids_to_new_item_ids.php @@ -35,6 +35,7 @@ public function up(): void if (! $childItemId || ! $childItemType) { $newBom[] = $entry; + continue; } @@ -47,6 +48,7 @@ public function up(): void if (! $sourceTable) { $newBom[] = $entry; + continue; } @@ -104,6 +106,7 @@ public function down(): void if (! $childItemId) { $newBom[] = $entry; + continue; } diff --git a/database/migrations/2025_12_18_100001_create_push_device_tokens_table.php b/database/migrations/2025_12_18_100001_create_push_device_tokens_table.php index b428475..ce1a1be 100644 --- a/database/migrations/2025_12_18_100001_create_push_device_tokens_table.php +++ b/database/migrations/2025_12_18_100001_create_push_device_tokens_table.php @@ -32,4 +32,4 @@ public function down(): void { Schema::dropIfExists('push_device_tokens'); } -}; \ No newline at end of file +}; diff --git a/database/migrations/2025_12_18_100002_create_push_notification_settings_table.php b/database/migrations/2025_12_18_100002_create_push_notification_settings_table.php index 7c9bf26..d63a8af 100644 --- a/database/migrations/2025_12_18_100002_create_push_notification_settings_table.php +++ b/database/migrations/2025_12_18_100002_create_push_notification_settings_table.php @@ -31,4 +31,4 @@ public function down(): void { Schema::dropIfExists('push_notification_settings'); } -}; \ No newline at end of file +}; diff --git a/routes/api.php b/routes/api.php index d4278b1..e28de01 100644 --- a/routes/api.php +++ b/routes/api.php @@ -3,6 +3,9 @@ use App\Http\Controllers\Api\Admin\GlobalMenuController; use App\Http\Controllers\Api\V1\AdminController; use App\Http\Controllers\Api\V1\ApiController; +use App\Http\Controllers\Api\V1\ApprovalController; +use App\Http\Controllers\Api\V1\ApprovalFormController; +use App\Http\Controllers\Api\V1\ApprovalLineController; use App\Http\Controllers\Api\V1\AttendanceController; use App\Http\Controllers\Api\V1\BankAccountController; use App\Http\Controllers\Api\V1\BoardController; @@ -36,23 +39,21 @@ use App\Http\Controllers\Api\V1\ItemMaster\ItemSectionController; use App\Http\Controllers\Api\V1\ItemMaster\SectionTemplateController; use App\Http\Controllers\Api\V1\ItemMaster\UnitOptionController; +// use App\Http\Controllers\Api\V1\MaterialController; // REMOVED: materials 테이블 삭제됨 use App\Http\Controllers\Api\V1\ItemsBomController; use App\Http\Controllers\Api\V1\ItemsController; use App\Http\Controllers\Api\V1\ItemsFileController; -// use App\Http\Controllers\Api\V1\MaterialController; // REMOVED: materials 테이블 삭제됨 -use App\Http\Controllers\Api\V1\ApprovalController; -use App\Http\Controllers\Api\V1\ApprovalFormController; -use App\Http\Controllers\Api\V1\ApprovalLineController; use App\Http\Controllers\Api\V1\LeaveController; use App\Http\Controllers\Api\V1\MenuController; use App\Http\Controllers\Api\V1\ModelSetController; +use App\Http\Controllers\Api\V1\PayrollController; use App\Http\Controllers\Api\V1\PermissionController; -use App\Http\Controllers\Api\V1\PostController; // use App\Http\Controllers\Api\V1\ProductBomItemController; // REMOVED: products 테이블 삭제됨 // use App\Http\Controllers\Api\V1\ProductController; // REMOVED: products 테이블 삭제됨 +use App\Http\Controllers\Api\V1\PostController; use App\Http\Controllers\Api\V1\PricingController; -use App\Http\Controllers\Api\V1\PushNotificationController; use App\Http\Controllers\Api\V1\PurchaseController; +use App\Http\Controllers\Api\V1\PushNotificationController; use App\Http\Controllers\Api\V1\QuoteController; use App\Http\Controllers\Api\V1\RefreshController; use App\Http\Controllers\Api\V1\RegisterController; @@ -62,18 +63,17 @@ use App\Http\Controllers\Api\V1\SaleController; use App\Http\Controllers\Api\V1\SiteController; use App\Http\Controllers\Api\V1\TenantController; -use App\Http\Controllers\Api\V1\TenantFieldSettingController; // 설계 전용 (디자인 네임스페이스) +use App\Http\Controllers\Api\V1\TenantFieldSettingController; use App\Http\Controllers\Api\V1\TenantOptionGroupController; use App\Http\Controllers\Api\V1\TenantOptionValueController; use App\Http\Controllers\Api\V1\TenantStatFieldController; use App\Http\Controllers\Api\V1\TenantUserProfileController; -use App\Http\Controllers\Api\V1\UserController; // 모델셋 관리 (견적 시스템) +use App\Http\Controllers\Api\V1\UserController; use App\Http\Controllers\Api\V1\UserRoleController; use App\Http\Controllers\Api\V1\WithdrawalController; use App\Http\Controllers\Api\V1\WorkSettingController; -use App\Http\Controllers\Api\V1\PayrollController; use Illuminate\Support\Facades\Route; // V1 초기 개발