From 9bae7fccae83ec5192d354c1138306ccf20a7019 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B6=8C=ED=98=81=EC=84=B1?= Date: Mon, 9 Feb 2026 16:55:34 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=ED=8A=B8=EB=A6=AC=EA=B1=B0=20=EA=B0=90?= =?UTF-8?q?=EC=82=AC=20=EB=A1=9C=EA=B7=B8=20Swagger=20=EB=AC=B8=EC=84=9C?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20api=5Frequest=5Flogs=20?= =?UTF-8?q?=EC=A0=9C=EC=99=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - TriggerAuditLogApi.php Swagger 파일 생성 (6개 엔드포인트 문서화) - 목록 조회, 통계, 상세, 레코드 이력, 롤백 미리보기, 롤백 실행 - api_request_logs를 트리거 제외 테이블 목록에 추가 - Pint 포매팅 적용 Co-Authored-By: Claude Opus 4.6 --- app/Swagger/v1/TriggerAuditLogApi.php | 234 +++ ...1_create_audit_triggers_for_all_tables.php | 10 +- storage/api-docs/api-docs-v1.json | 1761 +++++++++++++++++ 3 files changed, 2001 insertions(+), 4 deletions(-) create mode 100644 app/Swagger/v1/TriggerAuditLogApi.php diff --git a/app/Swagger/v1/TriggerAuditLogApi.php b/app/Swagger/v1/TriggerAuditLogApi.php new file mode 100644 index 0000000..34bfa3d --- /dev/null +++ b/app/Swagger/v1/TriggerAuditLogApi.php @@ -0,0 +1,234 @@ +excludeTables, true)) { $skipped++; + continue; } @@ -106,12 +108,12 @@ private function createTriggersForTable(string $dbName, string $tableName): void $pk = $pkRow->COLUMN_NAME; // 컬럼 목록 (제외 컬럼 필터링) - $columns = DB::select(" + $columns = DB::select(' SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ? ORDER BY ORDINAL_POSITION - ", [$dbName, $tableName]); + ', [$dbName, $tableName]); $cols = []; $hasTenantId = false; @@ -144,8 +146,8 @@ private function createTriggersForTable(string $dbName, string $tableName): void $cols )); - $tenantNew = $hasTenantId ? "NEW.`tenant_id`" : 'NULL'; - $tenantOld = $hasTenantId ? "OLD.`tenant_id`" : 'NULL'; + $tenantNew = $hasTenantId ? 'NEW.`tenant_id`' : 'NULL'; + $tenantOld = $hasTenantId ? 'OLD.`tenant_id`' : 'NULL'; // 기존 트리거 삭제 DB::unprepared("DROP TRIGGER IF EXISTS `trg_{$tableName}_ai`"); diff --git a/storage/api-docs/api-docs-v1.json b/storage/api-docs/api-docs-v1.json index 3750ac9..1b9fb4d 100755 --- a/storage/api-docs/api-docs-v1.json +++ b/storage/api-docs/api-docs-v1.json @@ -3123,6 +3123,141 @@ ] } }, + "/api/v1/app/version": { + "get": { + "tags": [ + "App Version" + ], + "summary": "최신 버전 확인", + "description": "현재 앱 버전과 서버의 최신 버전을 비교하여 업데이트 필요 여부를 반환합니다. Bearer 토큰 불필요, API Key만 필요합니다.", + "operationId": "9509c32f1c66e8817112c81571a761b2", + "parameters": [ + { + "name": "platform", + "in": "query", + "description": "플랫폼 (기본값: android)", + "required": false, + "schema": { + "type": "string", + "enum": [ + "android", + "ios" + ], + "example": "android" + } + }, + { + "name": "current_version_code", + "in": "query", + "description": "현재 앱의 버전 코드 (정수)", + "required": true, + "schema": { + "type": "integer", + "example": 1 + } + } + ], + "responses": { + "200": { + "description": "버전 확인 성공", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ApiResponse" + }, + { + "properties": { + "data": { + "$ref": "#/components/schemas/AppVersionCheckResponse" + } + }, + "type": "object" + } + ] + } + } + } + }, + "401": { + "description": "API Key 인증 실패", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/api/v1/app/download/{id}": { + "get": { + "tags": [ + "App Version" + ], + "summary": "APK 다운로드", + "description": "지정된 버전의 APK 파일을 다운로드합니다. 다운로드 카운트가 자동으로 증가합니다. Bearer 토큰 불필요, API Key만 필요합니다.", + "operationId": "6cb801087b59e4dbf5dc1ffdcf9d9068", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "앱 버전 ID", + "required": true, + "schema": { + "type": "integer", + "example": 3 + } + } + ], + "responses": { + "200": { + "description": "APK 파일 다운로드", + "content": { + "application/vnd.android.package-archive": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + }, + "404": { + "description": "APK 파일을 찾을 수 없음", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "API Key 인증 실패", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, "/api/v1/approvals/drafts": { "get": { "tags": [ @@ -20493,6 +20628,652 @@ ] } }, + "/api/v1/documents/{id}/submit": { + "post": { + "tags": [ + "Documents" + ], + "summary": "결재 제출", + "description": "DRAFT 또는 REJECTED 상태의 문서를 결재 요청합니다 (PENDING 상태로 변경).", + "operationId": "b4037b49b968c05841434356b0c9218e", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "문서 ID", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "결재 제출 성공", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ApiResponse" + }, + { + "properties": { + "data": { + "$ref": "#/components/schemas/Document" + } + }, + "type": "object" + } + ] + } + } + } + }, + "400": { + "description": "잘못된 요청 (제출 불가 상태 또는 결재선 미설정)", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "인증 실패", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "문서를 찾을 수 없음", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "서버 에러", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + }, + { + "BearerAuth": [] + } + ] + } + }, + "/api/v1/documents/{id}/approve": { + "post": { + "tags": [ + "Documents" + ], + "summary": "결재 승인", + "description": "현재 사용자의 결재 단계를 승인합니다. 모든 단계 완료 시 문서가 APPROVED 상태로 변경됩니다.", + "operationId": "5ab96b982b0953b93461c06e74c54a70", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "문서 ID", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": false, + "content": { + "application/json": { + "schema": { + "properties": { + "comment": { + "description": "결재 의견", + "type": "string", + "example": "승인합니다.", + "nullable": true + } + }, + "type": "object" + } + } + } + }, + "responses": { + "200": { + "description": "승인 성공", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ApiResponse" + }, + { + "properties": { + "data": { + "$ref": "#/components/schemas/Document" + } + }, + "type": "object" + } + ] + } + } + } + }, + "400": { + "description": "잘못된 요청 (승인 불가 상태 또는 차례 아님)", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "인증 실패", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "문서를 찾을 수 없음", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "서버 에러", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + }, + { + "BearerAuth": [] + } + ] + } + }, + "/api/v1/documents/{id}/reject": { + "post": { + "tags": [ + "Documents" + ], + "summary": "결재 반려", + "description": "현재 사용자의 결재 단계를 반려합니다. 문서가 REJECTED 상태로 변경됩니다.", + "operationId": "15213c9112db6e018e045dfbd645ca05", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "문서 ID", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "required": [ + "comment" + ], + "properties": { + "comment": { + "description": "반려 사유 (필수)", + "type": "string", + "example": "검사 기준 미달로 반려합니다." + } + }, + "type": "object" + } + } + } + }, + "responses": { + "200": { + "description": "반려 성공", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ApiResponse" + }, + { + "properties": { + "data": { + "$ref": "#/components/schemas/Document" + } + }, + "type": "object" + } + ] + } + } + } + }, + "400": { + "description": "잘못된 요청 (반려 불가 상태 또는 차례 아님)", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "인증 실패", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "문서를 찾을 수 없음", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "서버 에러", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + }, + { + "BearerAuth": [] + } + ] + } + }, + "/api/v1/documents/{id}/cancel": { + "post": { + "tags": [ + "Documents" + ], + "summary": "결재 취소/회수", + "description": "작성자만 DRAFT 또는 PENDING 상태의 문서를 취소할 수 있습니다. CANCELLED 상태로 변경됩니다.", + "operationId": "f95eb227a23d428d67a6022ebdac2d81", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "문서 ID", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "취소 성공", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ApiResponse" + }, + { + "properties": { + "data": { + "$ref": "#/components/schemas/Document" + } + }, + "type": "object" + } + ] + } + } + } + }, + "400": { + "description": "잘못된 요청 (취소 불가 상태 또는 작성자 아님)", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "인증 실패", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "문서를 찾을 수 없음", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "서버 에러", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + }, + { + "BearerAuth": [] + } + ] + } + }, + "/api/v1/document-templates": { + "get": { + "tags": [ + "DocumentTemplates" + ], + "summary": "양식 목록 조회", + "description": "문서 양식(템플릿) 목록을 조회합니다. 결재라인과 기본필드를 포함합니다.", + "operationId": "f6d1903439436f2911e32c00f7fb17f5", + "parameters": [ + { + "name": "is_active", + "in": "query", + "description": "활성 상태 필터", + "schema": { + "type": "boolean" + } + }, + { + "name": "category", + "in": "query", + "description": "카테고리 필터", + "schema": { + "type": "string", + "example": "품질" + } + }, + { + "name": "search", + "in": "query", + "description": "검색어 (양식명, 제목)", + "schema": { + "type": "string" + } + }, + { + "name": "sort_by", + "in": "query", + "description": "정렬 기준", + "schema": { + "type": "string", + "default": "created_at", + "enum": [ + "created_at", + "name", + "category" + ] + } + }, + { + "name": "sort_dir", + "in": "query", + "description": "정렬 방향", + "schema": { + "type": "string", + "default": "desc", + "enum": [ + "asc", + "desc" + ] + } + }, + { + "$ref": "#/components/parameters/Page" + }, + { + "$ref": "#/components/parameters/Size" + } + ], + "responses": { + "200": { + "description": "조회 성공", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ApiResponse" + }, + { + "properties": { + "data": { + "properties": { + "current_page": { + "type": "integer", + "example": 1 + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DocumentTemplate" + } + }, + "per_page": { + "type": "integer", + "example": 20 + }, + "total": { + "type": "integer", + "example": 10 + } + }, + "type": "object" + } + }, + "type": "object" + } + ] + } + } + } + }, + "400": { + "description": "잘못된 요청", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "인증 실패", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "서버 에러", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + }, + { + "BearerAuth": [] + } + ] + } + }, + "/api/v1/document-templates/{id}": { + "get": { + "tags": [ + "DocumentTemplates" + ], + "summary": "양식 상세 조회", + "description": "ID 기준 양식 상세 정보를 조회합니다. 결재라인, 기본필드, 섹션(검사항목 포함), 컬럼 전체를 반환합니다.", + "operationId": "1729eaeb3a2525c0ae5205933a78c9cb", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "양식 ID", + "required": true, + "schema": { + "type": "integer", + "example": 7 + } + } + ], + "responses": { + "200": { + "description": "조회 성공", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ApiResponse" + }, + { + "properties": { + "data": { + "$ref": "#/components/schemas/DocumentTemplate" + } + }, + "type": "object" + } + ] + } + } + } + }, + "401": { + "description": "인증 실패", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "양식을 찾을 수 없음", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "서버 에러", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + }, + { + "BearerAuth": [] + } + ] + } + }, "/api/v1/employees": { "get": { "tags": [ @@ -54693,6 +55474,506 @@ ] } }, + "/api/v1/trigger-audit-logs": { + "get": { + "tags": [ + "Trigger Audit" + ], + "summary": "감사 로그 목록 조회", + "description": "DB 트리거 기반 변경 로그를 페이지네이션으로 조회합니다.", + "operationId": "f8811286b399207c792047a90da1bfb1", + "parameters": [ + { + "name": "page", + "in": "query", + "schema": { + "type": "integer", + "minimum": 1 + } + }, + { + "name": "size", + "in": "query", + "schema": { + "type": "integer", + "maximum": 200, + "minimum": 1 + } + }, + { + "name": "table_name", + "in": "query", + "description": "테이블명 필터", + "schema": { + "type": "string" + } + }, + { + "name": "row_id", + "in": "query", + "description": "레코드 PK 필터", + "schema": { + "type": "string" + } + }, + { + "name": "dml_type", + "in": "query", + "description": "DML 유형 필터", + "schema": { + "type": "string", + "enum": [ + "INSERT", + "UPDATE", + "DELETE" + ] + } + }, + { + "name": "tenant_id", + "in": "query", + "schema": { + "type": "integer" + } + }, + { + "name": "actor_id", + "in": "query", + "schema": { + "type": "integer" + } + }, + { + "name": "db_user", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "from", + "in": "query", + "description": "시작일", + "schema": { + "type": "string", + "format": "date" + } + }, + { + "name": "to", + "in": "query", + "description": "종료일", + "schema": { + "type": "string", + "format": "date" + } + }, + { + "name": "sort", + "in": "query", + "schema": { + "type": "string", + "enum": [ + "created_at", + "id" + ] + } + }, + { + "name": "order", + "in": "query", + "schema": { + "type": "string", + "enum": [ + "asc", + "desc" + ] + } + } + ], + "responses": { + "200": { + "description": "목록 조회 성공", + "content": { + "application/json": { + "schema": { + "properties": { + "success": { + "type": "boolean" + }, + "message": { + "type": "string" + }, + "data": { + "properties": { + "current_page": { + "type": "integer" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TriggerAuditLog" + } + }, + "last_page": { + "type": "integer" + }, + "per_page": { + "type": "integer" + }, + "total": { + "type": "integer" + } + }, + "type": "object" + } + }, + "type": "object" + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [], + "BearerAuth": [] + } + ] + } + }, + "/api/v1/trigger-audit-logs/stats": { + "get": { + "tags": [ + "Trigger Audit" + ], + "summary": "감사 로그 통계", + "description": "전체/오늘/DML별 건수, 상위 테이블, 저장소 크기 통계를 반환합니다.", + "operationId": "6c9634c59472ce800617df1424865616", + "parameters": [ + { + "name": "tenant_id", + "in": "query", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "통계 조회 성공", + "content": { + "application/json": { + "schema": { + "properties": { + "success": { + "type": "boolean" + }, + "message": { + "type": "string" + }, + "data": { + "properties": { + "total": { + "type": "integer" + }, + "today": { + "type": "integer" + }, + "by_dml_type": { + "properties": { + "INSERT": { + "type": "integer" + }, + "UPDATE": { + "type": "integer" + }, + "DELETE": { + "type": "integer" + } + }, + "type": "object" + }, + "top_tables": { + "type": "object" + }, + "storage_mb": { + "type": "number" + } + }, + "type": "object" + } + }, + "type": "object" + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [], + "BearerAuth": [] + } + ] + } + }, + "/api/v1/trigger-audit-logs/{id}": { + "get": { + "tags": [ + "Trigger Audit" + ], + "summary": "감사 로그 상세 조회", + "operationId": "5b09fda6b7594f2ab85d3544b1d0850f", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "상세 조회 성공", + "content": { + "application/json": { + "schema": { + "properties": { + "success": { + "type": "boolean" + }, + "message": { + "type": "string" + }, + "data": { + "$ref": "#/components/schemas/TriggerAuditLog" + } + }, + "type": "object" + } + } + } + }, + "404": { + "description": "Not Found" + } + }, + "security": [ + { + "ApiKeyAuth": [], + "BearerAuth": [] + } + ] + } + }, + "/api/v1/trigger-audit-logs/{tableName}/{rowId}/history": { + "get": { + "tags": [ + "Trigger Audit" + ], + "summary": "레코드 변경 이력 조회", + "description": "특정 테이블의 특정 레코드에 대한 전체 변경 이력을 조회합니다.", + "operationId": "96643eef44f80bfb03dfae370a3e89cd", + "parameters": [ + { + "name": "tableName", + "in": "path", + "description": "테이블명", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "rowId", + "in": "path", + "description": "레코드 PK", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "이력 조회 성공", + "content": { + "application/json": { + "schema": { + "properties": { + "success": { + "type": "boolean" + }, + "message": { + "type": "string" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TriggerAuditLog" + } + } + }, + "type": "object" + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [], + "BearerAuth": [] + } + ] + } + }, + "/api/v1/trigger-audit-logs/{id}/rollback-preview": { + "get": { + "tags": [ + "Trigger Audit" + ], + "summary": "롤백 SQL 미리보기", + "description": "해당 변경을 되돌리기 위한 SQL문을 미리 확인합니다.", + "operationId": "b7c422185815b176bdabf91455cdfdd5", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "롤백 SQL 반환", + "content": { + "application/json": { + "schema": { + "properties": { + "success": { + "type": "boolean" + }, + "message": { + "type": "string" + }, + "data": { + "properties": { + "audit_id": { + "type": "integer" + }, + "rollback_sql": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object" + } + } + } + }, + "404": { + "description": "Not Found" + } + }, + "security": [ + { + "ApiKeyAuth": [], + "BearerAuth": [] + } + ] + } + }, + "/api/v1/trigger-audit-logs/{id}/rollback": { + "post": { + "tags": [ + "Trigger Audit" + ], + "summary": "롤백 실행", + "description": "해당 변경을 실제로 되돌립니다. confirm=true 필수.", + "operationId": "d32645b2f3f5ae258fc16575b2d301eb", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "required": [ + "confirm" + ], + "properties": { + "confirm": { + "description": "롤백 확인 (true 필수)", + "type": "boolean", + "example": true + } + }, + "type": "object" + } + } + } + }, + "responses": { + "200": { + "description": "롤백 성공", + "content": { + "application/json": { + "schema": { + "properties": { + "success": { + "type": "boolean" + }, + "message": { + "type": "string" + }, + "data": { + "properties": { + "rolled_back": { + "type": "boolean" + }, + "sql_executed": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object" + } + } + } + }, + "404": { + "description": "Not Found" + }, + "422": { + "description": "Validation Error" + } + }, + "security": [ + { + "ApiKeyAuth": [], + "BearerAuth": [] + } + ] + } + }, "/api/v1/users/me": { "get": { "tags": [ @@ -60373,6 +61654,75 @@ }, "type": "object" }, + "AppVersionLatest": { + "description": "최신 앱 버전 정보", + "properties": { + "id": { + "type": "integer", + "example": 3 + }, + "version_code": { + "description": "정수 비교용 버전 코드", + "type": "integer", + "example": 2 + }, + "version_name": { + "description": "표시용 버전명", + "type": "string", + "example": "0.2" + }, + "release_notes": { + "description": "변경사항", + "type": "string", + "example": "- 알림음 채널 정리\\n- 인앱 업데이트 추가", + "nullable": true + }, + "force_update": { + "description": "강제 업데이트 여부", + "type": "boolean", + "example": false + }, + "apk_size": { + "description": "APK 파일 크기(bytes)", + "type": "integer", + "example": 15728640, + "nullable": true + }, + "download_url": { + "description": "APK 다운로드 URL", + "type": "string", + "example": "https://api.codebridge-x.com/api/v1/app/download/3" + }, + "published_at": { + "description": "배포일", + "type": "string", + "format": "date", + "example": "2026-01-30", + "nullable": true + } + }, + "type": "object" + }, + "AppVersionCheckResponse": { + "description": "버전 확인 응답", + "properties": { + "has_update": { + "description": "업데이트 존재 여부", + "type": "boolean", + "example": true + }, + "latest_version": { + "oneOf": [ + { + "$ref": "#/components/schemas/AppVersionLatest" + } + ], + "nullable": true, + "description": "최신 버전 정보 (has_update=false면 null)" + } + }, + "type": "object" + }, "Approval": { "description": "결재 문서 정보", "properties": { @@ -69896,6 +71246,333 @@ }, "type": "object" }, + "DocumentTemplate": { + "description": "문서 양식 정보", + "properties": { + "id": { + "description": "양식 ID", + "type": "integer", + "example": 1 + }, + "tenant_id": { + "description": "테넌트 ID", + "type": "integer", + "example": 1 + }, + "name": { + "description": "양식명", + "type": "string", + "example": "수입검사 성적서 (EGI)" + }, + "category": { + "description": "분류", + "type": "string", + "example": "품질" + }, + "title": { + "description": "문서 제목", + "type": "string", + "example": "수입검사 성적서", + "nullable": true + }, + "company_name": { + "description": "회사명", + "type": "string", + "example": "(주)SAM", + "nullable": true + }, + "company_address": { + "description": "회사 주소", + "type": "string", + "nullable": true + }, + "company_contact": { + "description": "연락처", + "type": "string", + "nullable": true + }, + "footer_remark_label": { + "description": "하단 비고 라벨", + "type": "string", + "example": "부적합 내용" + }, + "footer_judgement_label": { + "description": "하단 판정 라벨", + "type": "string", + "example": "종합판정" + }, + "footer_judgement_options": { + "description": "판정 옵션", + "type": "array", + "items": { + "type": "string", + "example": "적합" + }, + "nullable": true + }, + "is_active": { + "description": "활성 여부", + "type": "boolean", + "example": true + }, + "approval_lines": { + "description": "결재라인", + "type": "array", + "items": { + "$ref": "#/components/schemas/DocumentTemplateApprovalLine" + } + }, + "basic_fields": { + "description": "기본 필드", + "type": "array", + "items": { + "$ref": "#/components/schemas/DocumentTemplateBasicField" + } + }, + "sections": { + "description": "검사 기준서 섹션", + "type": "array", + "items": { + "$ref": "#/components/schemas/DocumentTemplateSection" + } + }, + "columns": { + "description": "테이블 컬럼", + "type": "array", + "items": { + "$ref": "#/components/schemas/DocumentTemplateColumn" + } + }, + "created_at": { + "type": "string", + "format": "date-time", + "example": "2026-01-28T09:00:00Z" + }, + "updated_at": { + "type": "string", + "format": "date-time", + "example": "2026-01-28T09:00:00Z" + } + }, + "type": "object" + }, + "DocumentTemplateApprovalLine": { + "description": "양식 결재라인", + "properties": { + "id": { + "type": "integer", + "example": 1 + }, + "template_id": { + "type": "integer", + "example": 1 + }, + "name": { + "description": "결재자 이름/직책", + "type": "string", + "example": "작성" + }, + "dept": { + "description": "부서", + "type": "string", + "example": "품질", + "nullable": true + }, + "role": { + "description": "역할", + "type": "string", + "example": "작성" + }, + "sort_order": { + "description": "정렬 순서", + "type": "integer", + "example": 1 + } + }, + "type": "object" + }, + "DocumentTemplateBasicField": { + "description": "양식 기본 필드", + "properties": { + "id": { + "type": "integer", + "example": 1 + }, + "template_id": { + "type": "integer", + "example": 1 + }, + "label": { + "description": "필드 라벨", + "type": "string", + "example": "품명" + }, + "field_type": { + "description": "필드 타입", + "type": "string", + "example": "text" + }, + "default_value": { + "description": "기본값", + "type": "string", + "example": "", + "nullable": true + }, + "sort_order": { + "description": "정렬 순서", + "type": "integer", + "example": 1 + } + }, + "type": "object" + }, + "DocumentTemplateSection": { + "description": "양식 검사 기준서 섹션", + "properties": { + "id": { + "type": "integer", + "example": 1 + }, + "template_id": { + "type": "integer", + "example": 1 + }, + "title": { + "description": "섹션 제목", + "type": "string", + "example": "검사항목" + }, + "image_path": { + "description": "검사 기준 이미지", + "type": "string", + "example": "/img/inspection/screen_inspection.jpg", + "nullable": true + }, + "sort_order": { + "description": "정렬 순서", + "type": "integer", + "example": 1 + }, + "items": { + "description": "검사항목", + "type": "array", + "items": { + "$ref": "#/components/schemas/DocumentTemplateSectionItem" + } + } + }, + "type": "object" + }, + "DocumentTemplateSectionItem": { + "description": "양식 검사항목", + "properties": { + "id": { + "type": "integer", + "example": 1 + }, + "section_id": { + "type": "integer", + "example": 1 + }, + "category": { + "description": "항목 분류", + "type": "string", + "example": "외관", + "nullable": true + }, + "item": { + "description": "검사항목", + "type": "string", + "example": "표면 스크래치" + }, + "standard": { + "description": "검사기준", + "type": "string", + "example": "스크래치 없을 것", + "nullable": true + }, + "method": { + "description": "검사방식", + "type": "string", + "example": "육안검사", + "nullable": true + }, + "frequency": { + "description": "검사주기", + "type": "string", + "example": "전수", + "nullable": true + }, + "regulation": { + "description": "관련 규격", + "type": "string", + "nullable": true + }, + "sort_order": { + "description": "정렬 순서", + "type": "integer", + "example": 1 + } + }, + "type": "object" + }, + "DocumentTemplateColumn": { + "description": "양식 테이블 컬럼", + "properties": { + "id": { + "type": "integer", + "example": 1 + }, + "template_id": { + "type": "integer", + "example": 1 + }, + "label": { + "description": "컬럼 라벨", + "type": "string", + "example": "1차" + }, + "width": { + "description": "컬럼 너비", + "type": "string", + "example": "80px", + "nullable": true + }, + "column_type": { + "description": "컬럼 타입", + "type": "string", + "enum": [ + "text", + "check", + "complex", + "select", + "measurement" + ], + "example": "check" + }, + "group_name": { + "description": "그룹명", + "type": "string", + "example": "측정치", + "nullable": true + }, + "sub_labels": { + "description": "하위 라벨 (complex 타입)", + "type": "array", + "items": { + "type": "string", + "example": "n1" + }, + "nullable": true + }, + "sort_order": { + "description": "정렬 순서", + "type": "integer", + "example": 1 + } + }, + "type": "object" + }, "Employee": { "description": "사원 정보", "properties": { @@ -88380,6 +90057,78 @@ }, "type": "object" }, + "TriggerAuditLog": { + "properties": { + "id": { + "type": "integer", + "example": 1 + }, + "table_name": { + "type": "string", + "example": "products" + }, + "row_id": { + "type": "string", + "example": "42" + }, + "dml_type": { + "type": "string", + "enum": [ + "INSERT", + "UPDATE", + "DELETE" + ] + }, + "old_values": { + "type": "object", + "nullable": true + }, + "new_values": { + "type": "object", + "nullable": true + }, + "changed_columns": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true + }, + "tenant_id": { + "type": "integer", + "nullable": true + }, + "actor_id": { + "type": "integer", + "nullable": true + }, + "session_info": { + "properties": { + "ip": { + "type": "string" + }, + "ua": { + "type": "string" + }, + "route": { + "type": "string" + } + }, + "type": "object", + "nullable": true + }, + "db_user": { + "type": "string", + "example": "samuser@%", + "nullable": true + }, + "created_at": { + "type": "string", + "format": "date-time" + } + }, + "type": "object" + }, "Member": { "description": "회원 기본 정보", "required": [ @@ -91427,6 +93176,10 @@ "name": "Documents", "description": "문서 관리" }, + { + "name": "DocumentTemplates", + "description": "문서 양식(템플릿) 관리" + }, { "name": "Employees", "description": "사원 관리 (HR)" @@ -91603,6 +93356,10 @@ "name": "TodayIssue", "description": "CEO 대시보드 - 오늘의 이슈 리스트 API" }, + { + "name": "Trigger Audit", + "description": "DB 트리거 기반 데이터 변경 추적 로그" + }, { "name": "UserInvitation", "description": "사용자 초대 관리" @@ -91631,6 +93388,10 @@ "name": "Admin.GlobalMenu", "description": "Admin.GlobalMenu" }, + { + "name": "App Version", + "description": "App Version" + }, { "name": "BankTransaction", "description": "BankTransaction"