299 lines
14 KiB
Java
299 lines
14 KiB
Java
package com.erp.controller;
|
|
|
|
import com.erp.dto.ApiResponse;
|
|
import com.erp.service.DdlService;
|
|
import lombok.RequiredArgsConstructor;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import org.springframework.http.ResponseEntity;
|
|
import org.springframework.web.bind.annotation.*;
|
|
|
|
import java.time.Instant;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
|
|
@RestController
|
|
@RequestMapping("/api/ddl")
|
|
@RequiredArgsConstructor
|
|
@Slf4j
|
|
public class DdlController {
|
|
|
|
private final DdlService ddlService;
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
// DDL 실행 (SUPER_ADMIN 전용)
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
|
|
/**
|
|
* POST /api/ddl/tables - 새 테이블 생성
|
|
*/
|
|
@PostMapping("/tables")
|
|
public ResponseEntity<ApiResponse<?>> createTable(
|
|
@RequestAttribute("company_code") String companyCode,
|
|
@RequestAttribute("user_id") String userId,
|
|
@RequestBody Map<String, Object> body) {
|
|
|
|
if (!isSuperAdmin(companyCode)) {
|
|
return ResponseEntity.status(403).body(ApiResponse.error("최고 관리자 권한이 필요합니다."));
|
|
}
|
|
|
|
String tableName = (String) body.get("table_name");
|
|
@SuppressWarnings("unchecked")
|
|
List<Map<String, Object>> columns = (List<Map<String, Object>>) body.get("columns");
|
|
String description = (String) body.get("description");
|
|
|
|
if (tableName == null || tableName.isBlank() || columns == null || columns.isEmpty()) {
|
|
return ResponseEntity.status(400)
|
|
.body(ApiResponse.error("테이블명과 최소 1개의 컬럼이 필요합니다."));
|
|
}
|
|
|
|
Map<String, Object> result = ddlService.createTable(tableName, columns, companyCode, userId, description);
|
|
|
|
if (Boolean.TRUE.equals(result.get("success"))) {
|
|
return ResponseEntity.ok(ApiResponse.success(Map.of(
|
|
"table_name", result.get("table_name"),
|
|
"column_count", result.get("column_count"),
|
|
"executed_query", result.get("executed_query")
|
|
), (String) result.get("message")));
|
|
}
|
|
return ResponseEntity.status(400).body(ApiResponse.error((String) result.get("message")));
|
|
}
|
|
|
|
/**
|
|
* POST /api/ddl/tables/{tableName}/columns - 컬럼 추가
|
|
*/
|
|
@PostMapping("/tables/{tableName}/columns")
|
|
public ResponseEntity<ApiResponse<?>> addColumn(
|
|
@PathVariable String tableName,
|
|
@RequestAttribute("company_code") String companyCode,
|
|
@RequestAttribute("user_id") String userId,
|
|
@RequestBody Map<String, Object> body) {
|
|
|
|
if (!isSuperAdmin(companyCode)) {
|
|
return ResponseEntity.status(403).body(ApiResponse.error("최고 관리자 권한이 필요합니다."));
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
Map<String, Object> column = (Map<String, Object>) body.get("column");
|
|
if (column == null || column.get("name") == null
|
|
|| column.get("name").toString().isBlank()) {
|
|
return ResponseEntity.status(400).body(ApiResponse.error("컬럼명이 필요합니다."));
|
|
}
|
|
|
|
Map<String, Object> result = ddlService.addColumn(tableName, column, companyCode, userId);
|
|
|
|
if (Boolean.TRUE.equals(result.get("success"))) {
|
|
return ResponseEntity.ok(ApiResponse.success(Map.of(
|
|
"table_name", result.get("table_name"),
|
|
"column_name", result.get("column_name"),
|
|
"executed_query", result.get("executed_query")
|
|
), (String) result.get("message")));
|
|
}
|
|
return ResponseEntity.status(400).body(ApiResponse.error((String) result.get("message")));
|
|
}
|
|
|
|
/**
|
|
* DELETE /api/ddl/tables/{tableName} - 테이블 삭제
|
|
*/
|
|
@DeleteMapping("/tables/{tableName}")
|
|
public ResponseEntity<ApiResponse<?>> dropTable(
|
|
@PathVariable String tableName,
|
|
@RequestAttribute("company_code") String companyCode,
|
|
@RequestAttribute("user_id") String userId) {
|
|
|
|
if (!isSuperAdmin(companyCode)) {
|
|
return ResponseEntity.status(403).body(ApiResponse.error("최고 관리자 권한이 필요합니다."));
|
|
}
|
|
|
|
Map<String, Object> result = ddlService.dropTable(tableName, companyCode, userId);
|
|
|
|
if (Boolean.TRUE.equals(result.get("success"))) {
|
|
return ResponseEntity.ok(ApiResponse.success(Map.of(
|
|
"table_name", result.get("table_name"),
|
|
"executed_query", result.get("executed_query")
|
|
), (String) result.get("message")));
|
|
}
|
|
return ResponseEntity.status(400).body(ApiResponse.error((String) result.get("message")));
|
|
}
|
|
|
|
/**
|
|
* POST /api/ddl/validate/table - 테이블 생성 사전 검증 (실제 생성 없음)
|
|
*/
|
|
@PostMapping("/validate/table")
|
|
public ResponseEntity<ApiResponse<?>> validateTableCreation(
|
|
@RequestAttribute("company_code") String companyCode,
|
|
@RequestBody Map<String, Object> body) {
|
|
|
|
if (!isSuperAdmin(companyCode)) {
|
|
return ResponseEntity.status(403).body(ApiResponse.error("최고 관리자 권한이 필요합니다."));
|
|
}
|
|
|
|
String tableName = (String) body.get("table_name");
|
|
@SuppressWarnings("unchecked")
|
|
List<Map<String, Object>> columns = (List<Map<String, Object>>) body.get("columns");
|
|
|
|
if (tableName == null || columns == null) {
|
|
return ResponseEntity.status(400)
|
|
.body(ApiResponse.error("테이블명과 컬럼 정보가 필요합니다."));
|
|
}
|
|
|
|
Map<String, Object> result = ddlService.validateTableCreation(tableName, columns);
|
|
return ResponseEntity.ok(ApiResponse.success(result, "검증 완료"));
|
|
}
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
// 로그 및 모니터링
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
|
|
/**
|
|
* GET /api/ddl/logs - DDL 실행 로그 조회
|
|
*/
|
|
@GetMapping("/logs")
|
|
public ResponseEntity<ApiResponse<?>> getDdlLogs(
|
|
@RequestAttribute("company_code") String companyCode,
|
|
@RequestParam(required = false, defaultValue = "50") int limit,
|
|
@RequestParam(required = false) String userId,
|
|
@RequestParam(required = false) String ddlType) {
|
|
|
|
if (!isSuperAdmin(companyCode)) {
|
|
return ResponseEntity.status(403).body(ApiResponse.error("최고 관리자 권한이 필요합니다."));
|
|
}
|
|
|
|
List<Map<String, Object>> logs = ddlService.getDdlLogs(limit, userId, ddlType);
|
|
return ResponseEntity.ok(ApiResponse.success(
|
|
Map.of("logs", logs, "total", logs.size()), "DDL 로그 조회 성공"));
|
|
}
|
|
|
|
/**
|
|
* GET /api/ddl/statistics - DDL 실행 통계 조회
|
|
*/
|
|
@GetMapping("/statistics")
|
|
public ResponseEntity<ApiResponse<?>> getDdlStatistics(
|
|
@RequestAttribute("company_code") String companyCode,
|
|
@RequestParam(required = false) String fromDate,
|
|
@RequestParam(required = false) String toDate) {
|
|
|
|
if (!isSuperAdmin(companyCode)) {
|
|
return ResponseEntity.status(403).body(ApiResponse.error("최고 관리자 권한이 필요합니다."));
|
|
}
|
|
|
|
Map<String, Object> statistics = ddlService.getDdlStatistics(fromDate, toDate);
|
|
return ResponseEntity.ok(ApiResponse.success(statistics, "DDL 통계 조회 성공"));
|
|
}
|
|
|
|
/**
|
|
* GET /api/ddl/tables/{tableName}/history - 테이블 DDL 히스토리 조회
|
|
*/
|
|
@GetMapping("/tables/{tableName}/history")
|
|
public ResponseEntity<ApiResponse<?>> getTableDdlHistory(
|
|
@PathVariable String tableName,
|
|
@RequestAttribute("company_code") String companyCode) {
|
|
|
|
if (!isSuperAdmin(companyCode)) {
|
|
return ResponseEntity.status(403).body(ApiResponse.error("최고 관리자 권한이 필요합니다."));
|
|
}
|
|
|
|
List<Map<String, Object>> history = ddlService.getTableDdlHistory(tableName);
|
|
return ResponseEntity.ok(ApiResponse.success(
|
|
Map.of("table_name", tableName, "history", history, "total", history.size()),
|
|
"테이블 DDL 히스토리 조회 성공"));
|
|
}
|
|
|
|
/**
|
|
* GET /api/ddl/tables/{tableName}/info - 테이블 정보 조회
|
|
*/
|
|
@GetMapping("/tables/{tableName}/info")
|
|
public ResponseEntity<ApiResponse<?>> getTableInfo(
|
|
@PathVariable String tableName,
|
|
@RequestAttribute("company_code") String companyCode) {
|
|
|
|
if (!isSuperAdmin(companyCode)) {
|
|
return ResponseEntity.status(403).body(ApiResponse.error("최고 관리자 권한이 필요합니다."));
|
|
}
|
|
|
|
Map<String, Object> tableInfo = ddlService.getTableInfo(tableName);
|
|
if (tableInfo == null) {
|
|
return ResponseEntity.status(404)
|
|
.body(ApiResponse.error("테이블 '" + tableName + "'을 찾을 수 없습니다."));
|
|
}
|
|
return ResponseEntity.ok(ApiResponse.success(tableInfo, "테이블 정보 조회 성공"));
|
|
}
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
// 시스템 관리
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
|
|
/**
|
|
* DELETE /api/ddl/logs/cleanup - 오래된 DDL 로그 정리
|
|
* NOTE: /logs/cleanup 은 /logs/{id} 보다 우선 매칭 (리터럴 세그먼트 우선)
|
|
*/
|
|
@DeleteMapping("/logs/cleanup")
|
|
public ResponseEntity<ApiResponse<?>> cleanupOldLogs(
|
|
@RequestAttribute("company_code") String companyCode,
|
|
@RequestParam(required = false, defaultValue = "90") int retentionDays) {
|
|
|
|
if (!isSuperAdmin(companyCode)) {
|
|
return ResponseEntity.status(403).body(ApiResponse.error("최고 관리자 권한이 필요합니다."));
|
|
}
|
|
|
|
int deletedCount = ddlService.cleanupOldLogs(retentionDays);
|
|
return ResponseEntity.ok(ApiResponse.success(
|
|
Map.of("deleted_count", deletedCount, "retention_days", retentionDays),
|
|
deletedCount + "개의 오래된 DDL 로그가 삭제되었습니다."));
|
|
}
|
|
|
|
/**
|
|
* GET /api/ddl/info - DDL 서비스 정보
|
|
*/
|
|
@GetMapping("/info")
|
|
public ResponseEntity<ApiResponse<?>> getInfo(
|
|
@RequestAttribute("company_code") String companyCode) {
|
|
|
|
if (!isSuperAdmin(companyCode)) {
|
|
return ResponseEntity.status(403).body(ApiResponse.error("최고 관리자 권한이 필요합니다."));
|
|
}
|
|
|
|
return ResponseEntity.ok(ApiResponse.success(Map.of(
|
|
"service", "DDL Execution Service",
|
|
"version", "1.0.0",
|
|
"description", "PostgreSQL 테이블 및 컬럼 동적 생성 서비스",
|
|
"requirements", Map.of(
|
|
"authentication", "Bearer Token 필요",
|
|
"authorization", "회사코드 '*'인 SUPER_ADMIN만 접근 가능",
|
|
"safety", "모든 DDL 실행은 안전성 검증 후 수행",
|
|
"logging", "모든 DDL 실행은 감사 로그에 기록"
|
|
),
|
|
"endpoints", Map.of(
|
|
"create_table", "POST /api/ddl/tables",
|
|
"add_column", "POST /api/ddl/tables/{tableName}/columns",
|
|
"drop_table", "DELETE /api/ddl/tables/{tableName}",
|
|
"validate", "POST /api/ddl/validate/table",
|
|
"logs", "GET /api/ddl/logs",
|
|
"statistics", "GET /api/ddl/statistics",
|
|
"history", "GET /api/ddl/tables/{tableName}/history",
|
|
"table_info", "GET /api/ddl/tables/{tableName}/info",
|
|
"cleanup", "DELETE /api/ddl/logs/cleanup"
|
|
)
|
|
), "DDL 서비스 정보"));
|
|
}
|
|
|
|
/**
|
|
* GET /api/ddl/health - 헬스체크 (인증만 확인, SUPER_ADMIN 불필요)
|
|
*/
|
|
@GetMapping("/health")
|
|
public ResponseEntity<ApiResponse<?>> health() {
|
|
return ResponseEntity.ok(ApiResponse.success(Map.of(
|
|
"status", "healthy",
|
|
"timestamp", Instant.now().toString(),
|
|
"checks", Map.of("service", "operational")
|
|
), "DDL 서비스 정상"));
|
|
}
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
// 내부 유틸
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
|
|
private boolean isSuperAdmin(String companyCode) {
|
|
return "*".equals(companyCode);
|
|
}
|
|
}
|