Files
invyone/backend-spring/src/main/java/com/erp/controller/TableManagementController.java
T
johngreen acbab68a12 chore(테이블관리): 사소 3건 정리 (PR-D) — 디버그 로그 / dead code / 에러 메시지
1. 운영 console.log/warn 제거 (G15) — page.tsx 의 이모지 prefix 디버그 로그
   (🔍 🔄  🗑️ 📥 📊 📋) 일괄 제거. catch 블록의 console.error 는 추적용으로
   유지. CreateTableModal 의 컬럼 조회 디버그 로그도 정리.

2. useLogTable dead code 정리 (G16) — CreateTableModal 의 useLogTable state,
   handleCreateTable 분기, 주석 처리된 체크박스 UI 모두 제거. 시그니처 안 맞는
   createLogTable 호출 페이로드까지 같이 사라짐. Activity / Checkbox import 도
   필요 없어졌으므로 제거.

3. 에러 메시지 일관화 (G17) — DdlController 와 TableManagementController 의
   "최고 관리자 권한이 필요합니다." 메시지 모두 "최고 관리자(SUPER_ADMIN) 권한이
   필요합니다." 로 통일. 일반 ADMIN 권한 메시지는 그대로 유지.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 15:11:21 +09:00

712 lines
39 KiB
Java

package com.erp.controller;
import com.erp.dto.ApiResponse;
import com.erp.service.TableManagementService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/table-management")
@RequiredArgsConstructor
@Slf4j
public class TableManagementController {
private final TableManagementService tableManagementService;
// ──────────────────────────────────────────────────────────
// 테이블 목록
// ──────────────────────────────────────────────────────────
/** GET /api/table-management/tables */
@GetMapping("/tables")
public ResponseEntity<ApiResponse<List<Map<String, Object>>>> getTableList() {
return ResponseEntity.ok(ApiResponse.success(
tableManagementService.getTableList(), "테이블 목록을 성공적으로 조회했습니다."));
}
// ──────────────────────────────────────────────────────────
// 엔티티 관계 (literal segment이 path variable보다 우선)
// ──────────────────────────────────────────────────────────
/** GET /api/table-management/tables/entity-relations */
@GetMapping("/tables/entity-relations")
public ResponseEntity<ApiResponse<Map<String, Object>>> getTableEntityRelations(
@RequestParam String leftTable,
@RequestParam String rightTable,
@RequestAttribute("company_code") String companyCode) {
return ResponseEntity.ok(ApiResponse.success(
tableManagementService.getTableEntityRelations(leftTable, rightTable, companyCode)));
}
// ──────────────────────────────────────────────────────────
// 컬럼 목록
// ──────────────────────────────────────────────────────────
/** GET /api/table-management/tables/:tableName/columns */
@GetMapping("/tables/{tableName}/columns")
public ResponseEntity<ApiResponse<Map<String, Object>>> getColumnList(
@PathVariable String tableName,
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "50") int size,
@RequestAttribute("company_code") String companyCode) {
return ResponseEntity.ok(ApiResponse.success(
tableManagementService.getColumnList(tableName, page, size, companyCode),
"컬럼 목록을 성공적으로 조회했습니다."));
}
// ──────────────────────────────────────────────────────────
// 테이블 라벨
// ──────────────────────────────────────────────────────────
/** GET /api/table-management/tables/:tableName/labels */
@GetMapping("/tables/{tableName}/labels")
public ResponseEntity<ApiResponse<Object>> getTableLabels(@PathVariable String tableName) {
Map<String, Object> labels = tableManagementService.getTableLabels(tableName);
Object data = labels != null ? labels : Map.of();
return ResponseEntity.ok(ApiResponse.success(data, "테이블 라벨 정보를 조회했습니다."));
}
/** PUT /api/table-management/tables/:tableName/label */
@PutMapping("/tables/{tableName}/label")
public ResponseEntity<ApiResponse<Void>> updateTableLabel(
@PathVariable String tableName,
@RequestBody Map<String, Object> body,
@RequestAttribute("role") String role) {
if (!isAdmin(role)) {
return ResponseEntity.status(403).body(ApiResponse.error("관리자 권한이 필요합니다."));
}
String displayName = (String) body.get("display_name");
String description = (String) body.get("description");
if (displayName == null || displayName.isBlank()) {
return ResponseEntity.status(400).body(ApiResponse.error("표시명이 필요합니다."));
}
tableManagementService.updateTableLabel(tableName, displayName, description);
return ResponseEntity.ok(ApiResponse.success(null, "테이블 라벨이 성공적으로 설정되었습니다."));
}
// ──────────────────────────────────────────────────────────
// 컬럼 설정
// ──────────────────────────────────────────────────────────
/** GET /api/table-management/tables/:tableName/columns/:columnName/labels */
@GetMapping("/tables/{tableName}/columns/{columnName}/labels")
public ResponseEntity<ApiResponse<Object>> getColumnLabels(
@PathVariable String tableName,
@PathVariable String columnName) {
Map<String, Object> labels = tableManagementService.getColumnLabels(tableName, columnName);
Object data = labels != null ? labels : Map.of();
return ResponseEntity.ok(ApiResponse.success(data, "컬럼 라벨 정보를 조회했습니다."));
}
/** POST /api/table-management/tables/:tableName/columns/:columnName/settings */
@PostMapping("/tables/{tableName}/columns/{columnName}/settings")
public ResponseEntity<ApiResponse<Void>> updateColumnSettingsPost(
@PathVariable String tableName,
@PathVariable String columnName,
@RequestBody Map<String, Object> settings,
@RequestAttribute("role") String role,
@RequestAttribute("company_code") String companyCode) {
if (!isAdmin(role)) {
return ResponseEntity.status(403).body(ApiResponse.error("관리자 권한이 필요합니다."));
}
return doUpdateColumnSettings(tableName, columnName, settings, companyCode);
}
/** PUT /api/table-management/tables/:tableName/columns/:columnName */
@PutMapping("/tables/{tableName}/columns/{columnName}")
public ResponseEntity<ApiResponse<Void>> updateColumnSettingsPut(
@PathVariable String tableName,
@PathVariable String columnName,
@RequestBody Map<String, Object> settings,
@RequestAttribute("role") String role,
@RequestAttribute("company_code") String companyCode) {
if (!isAdmin(role)) {
return ResponseEntity.status(403).body(ApiResponse.error("관리자 권한이 필요합니다."));
}
return doUpdateColumnSettings(tableName, columnName, settings, companyCode);
}
private ResponseEntity<ApiResponse<Void>> doUpdateColumnSettings(
String tableName, String columnName, Map<String, Object> settings, String companyCode) {
if (tableName == null || columnName == null) {
return ResponseEntity.status(400).body(ApiResponse.error("테이블명과 컬럼명이 필요합니다."));
}
if (settings == null) {
return ResponseEntity.status(400).body(ApiResponse.error("컬럼 설정 정보가 필요합니다."));
}
tableManagementService.updateColumnSettings(tableName, columnName, settings, companyCode);
return ResponseEntity.ok(ApiResponse.success(null, "컬럼 설정을 성공적으로 저장했습니다."));
}
/** POST /api/table-management/tables/:tableName/columns/settings (batch) */
@PostMapping("/tables/{tableName}/columns/settings")
public ResponseEntity<ApiResponse<Void>> updateAllColumnSettingsPost(
@PathVariable String tableName,
@RequestBody List<Map<String, Object>> columnSettings,
@RequestAttribute("role") String role,
@RequestAttribute("company_code") String companyCode) {
if (!isAdmin(role)) {
return ResponseEntity.status(403).body(ApiResponse.error("관리자 권한이 필요합니다."));
}
return doUpdateAllColumnSettings(tableName, columnSettings, companyCode);
}
/** PUT /api/table-management/tables/:tableName/columns/batch */
@PutMapping("/tables/{tableName}/columns/batch")
public ResponseEntity<ApiResponse<Void>> updateAllColumnSettingsBatch(
@PathVariable String tableName,
@RequestBody List<Map<String, Object>> columnSettings,
@RequestAttribute("role") String role,
@RequestAttribute("company_code") String companyCode) {
if (!isAdmin(role)) {
return ResponseEntity.status(403).body(ApiResponse.error("관리자 권한이 필요합니다."));
}
return doUpdateAllColumnSettings(tableName, columnSettings, companyCode);
}
private ResponseEntity<ApiResponse<Void>> doUpdateAllColumnSettings(
String tableName, List<Map<String, Object>> columnSettings, String companyCode) {
if (tableName == null) {
return ResponseEntity.status(400).body(ApiResponse.error("테이블명이 필요합니다."));
}
if (columnSettings == null || columnSettings.isEmpty()) {
return ResponseEntity.status(400).body(ApiResponse.error("컬럼 설정 목록이 필요합니다."));
}
tableManagementService.updateAllColumnSettings(tableName, columnSettings, companyCode);
return ResponseEntity.ok(ApiResponse.success(null, "모든 컬럼 설정을 성공적으로 저장했습니다."));
}
/** PUT /api/table-management/tables/:tableName/columns/:columnName/web-type */
@PutMapping("/tables/{tableName}/columns/{columnName}/web-type")
public ResponseEntity<ApiResponse<Void>> updateColumnWebType(
@PathVariable String tableName,
@PathVariable String columnName,
@RequestBody Map<String, Object> body,
@RequestAttribute("role") String role,
@RequestAttribute("company_code") String companyCode) {
if (!isAdmin(role)) {
return ResponseEntity.status(403).body(ApiResponse.error("관리자 권한이 필요합니다."));
}
String webType = (String) body.get("web_type");
if (webType == null || webType.isBlank()) {
return ResponseEntity.status(400).body(ApiResponse.error("웹 타입이 필요합니다."));
}
@SuppressWarnings("unchecked")
Map<String, Object> detailSettings = (Map<String, Object>) body.get("detail_settings");
// 멀티테넌트 격리: SUPER_ADMIN(company_code='*') 가 아니면 자기 회사 코드로 저장
tableManagementService.updateColumnWebType(tableName, columnName, webType, detailSettings, companyCode);
return ResponseEntity.ok(ApiResponse.success(null, "컬럼 웹타입이 설정되었습니다."));
}
/** PUT /api/table-management/tables/:tableName/columns/:columnName/input-type */
@PutMapping("/tables/{tableName}/columns/{columnName}/input-type")
public ResponseEntity<ApiResponse<Void>> updateColumnInputType(
@PathVariable String tableName,
@PathVariable String columnName,
@RequestBody Map<String, Object> body,
@RequestAttribute("role") String role,
@RequestAttribute("company_code") String companyCode) {
if (!isAdmin(role)) {
return ResponseEntity.status(403).body(ApiResponse.error("관리자 권한이 필요합니다."));
}
String inputType = (String) body.get("input_type");
if (tableName == null || columnName == null || inputType == null || inputType.isBlank()) {
return ResponseEntity.status(400).body(ApiResponse.error("테이블명, 컬럼명, 입력 타입이 모두 필요합니다."));
}
@SuppressWarnings("unchecked")
Map<String, Object> detailSettings = (Map<String, Object>) body.get("detail_settings");
tableManagementService.updateColumnInputType(tableName, columnName, inputType, companyCode, detailSettings);
return ResponseEntity.ok(ApiResponse.success(null, "컬럼 입력 타입이 성공적으로 설정되었습니다."));
}
// ──────────────────────────────────────────────────────────
// 테이블 스키마 / 존재 여부 / 웹타입
// ──────────────────────────────────────────────────────────
/** GET /api/table-management/tables/:tableName/schema */
@GetMapping("/tables/{tableName}/schema")
public ResponseEntity<ApiResponse<List<Map<String, Object>>>> getTableSchema(
@PathVariable String tableName) {
return ResponseEntity.ok(ApiResponse.success(
tableManagementService.getTableSchema(tableName),
"테이블 스키마 정보를 성공적으로 조회했습니다."));
}
/** GET /api/table-management/tables/:tableName/exists */
@GetMapping("/tables/{tableName}/exists")
public ResponseEntity<ApiResponse<Map<String, Object>>> checkTableExists(
@PathVariable String tableName) {
boolean exists = tableManagementService.checkTableExists(tableName);
return ResponseEntity.ok(ApiResponse.success(Map.of("exists", exists), "테이블 존재 여부를 확인했습니다."));
}
/** GET /api/table-management/tables/:tableName/web-types */
@GetMapping("/tables/{tableName}/web-types")
public ResponseEntity<ApiResponse<List<Map<String, Object>>>> getColumnWebTypes(
@PathVariable String tableName,
@RequestAttribute("company_code") String companyCode) {
return ResponseEntity.ok(ApiResponse.success(
tableManagementService.getColumnInputTypes(tableName, companyCode),
"컬럼 입력타입 정보를 성공적으로 조회했습니다."));
}
// ──────────────────────────────────────────────────────────
// 제약조건 관리
// ──────────────────────────────────────────────────────────
/** GET /api/table-management/tables/:tableName/constraints */
@GetMapping("/tables/{tableName}/constraints")
public ResponseEntity<ApiResponse<Map<String, Object>>> getTableConstraints(
@PathVariable String tableName) {
return ResponseEntity.ok(ApiResponse.success(
tableManagementService.getTableConstraints(tableName)));
}
/** PUT /api/table-management/tables/:tableName/primary-key */
@PutMapping("/tables/{tableName}/primary-key")
public ResponseEntity<ApiResponse<Void>> setTablePrimaryKey(
@PathVariable String tableName,
@RequestBody Map<String, Object> body,
@RequestAttribute("role") String role) {
if (!isSuperAdmin(role)) {
return ResponseEntity.status(403).body(ApiResponse.error("최고 관리자(SUPER_ADMIN) 권한이 필요합니다."));
}
@SuppressWarnings("unchecked")
List<String> columns = (List<String>) body.get("columns");
if (tableName == null || columns == null || columns.isEmpty()) {
return ResponseEntity.status(400).body(ApiResponse.error("테이블명과 PK 컬럼 배열이 필요합니다."));
}
tableManagementService.setTablePrimaryKey(tableName, columns);
return ResponseEntity.ok(ApiResponse.success(null,
"PK가 설정되었습니다: " + String.join(", ", columns)));
}
/** POST /api/table-management/tables/:tableName/indexes */
@PostMapping("/tables/{tableName}/indexes")
public ResponseEntity<ApiResponse<Void>> toggleTableIndex(
@PathVariable String tableName,
@RequestBody Map<String, Object> body,
@RequestAttribute("role") String role) {
if (!isSuperAdmin(role)) {
return ResponseEntity.status(403).body(ApiResponse.error("최고 관리자(SUPER_ADMIN) 권한이 필요합니다."));
}
String columnName = (String) body.get("column_name");
String indexType = (String) body.get("index_type");
String action = (String) body.get("action");
if (tableName == null || columnName == null || indexType == null || action == null) {
return ResponseEntity.status(400).body(ApiResponse.error(
"tableName, columnName, indexType(index|unique), action(create|drop)이 필요합니다."));
}
try {
tableManagementService.toggleTableIndex(tableName, columnName, indexType, action);
String msg = "create".equals(action)
? "인덱스가 생성되었습니다."
: "인덱스가 삭제되었습니다.";
return ResponseEntity.ok(ApiResponse.success(null, msg));
} catch (Exception e) {
return ResponseEntity.status(500).body(ApiResponse.error(e.getMessage()));
}
}
/** PUT /api/table-management/tables/:tableName/columns/:columnName/nullable */
@PutMapping("/tables/{tableName}/columns/{columnName}/nullable")
public ResponseEntity<ApiResponse<Void>> toggleColumnNullable(
@PathVariable String tableName,
@PathVariable String columnName,
@RequestBody Map<String, Object> body,
@RequestAttribute("role") String role,
@RequestAttribute("company_code") String companyCode) {
if (!isSuperAdmin(role)) {
return ResponseEntity.status(403).body(ApiResponse.error("최고 관리자(SUPER_ADMIN) 권한이 필요합니다."));
}
Object nullableObj = body.get("nullable");
if (tableName == null || columnName == null || !(nullableObj instanceof Boolean)) {
return ResponseEntity.status(400).body(ApiResponse.error("tableName, columnName, nullable(boolean)이 필요합니다."));
}
boolean nullable = (Boolean) nullableObj;
tableManagementService.toggleColumnNullable(tableName, columnName, nullable, companyCode);
String msg = nullable ? columnName + " 컬럼의 NOT NULL 제약이 해제되었습니다."
: columnName + " 컬럼이 NOT NULL로 설정되었습니다.";
return ResponseEntity.ok(ApiResponse.success(null, msg));
}
/** PUT /api/table-management/tables/:tableName/columns/:columnName/unique */
@PutMapping("/tables/{tableName}/columns/{columnName}/unique")
public ResponseEntity<ApiResponse<Void>> toggleColumnUnique(
@PathVariable String tableName,
@PathVariable String columnName,
@RequestBody Map<String, Object> body,
@RequestAttribute("role") String role,
@RequestAttribute("company_code") String companyCode) {
if (!isSuperAdmin(role)) {
return ResponseEntity.status(403).body(ApiResponse.error("최고 관리자(SUPER_ADMIN) 권한이 필요합니다."));
}
Object uniqueObj = body.get("unique");
if (tableName == null || columnName == null || !(uniqueObj instanceof Boolean)) {
return ResponseEntity.status(400).body(ApiResponse.error("tableName, columnName, unique(boolean)이 필요합니다."));
}
boolean unique = (Boolean) uniqueObj;
tableManagementService.toggleColumnUnique(tableName, columnName, unique, companyCode);
String msg = unique ? columnName + " 컬럼이 UNIQUE로 설정되었습니다."
: columnName + " 컬럼의 UNIQUE 제약이 해제되었습니다.";
return ResponseEntity.ok(ApiResponse.success(null, msg));
}
// ──────────────────────────────────────────────────────────
// 데이터 CRUD
// ──────────────────────────────────────────────────────────
/** POST /api/table-management/tables/:tableName/data */
@PostMapping("/tables/{tableName}/data")
public ResponseEntity<ApiResponse<Map<String, Object>>> getTableData(
@PathVariable String tableName,
@RequestBody Map<String, Object> options) {
return ResponseEntity.ok(ApiResponse.success(
tableManagementService.getTableData(tableName, options),
"테이블 데이터를 성공적으로 조회했습니다."));
}
/** POST /api/table-management/tables/:tableName/aggregate
* body: { aggregation: "count"|"sum"|..., columnName?: string, filters?: [...] }
* → { value: number }
*/
@PostMapping("/tables/{tableName}/aggregate")
public ResponseEntity<ApiResponse<Map<String, Object>>> aggregateTableData(
@PathVariable String tableName,
@RequestBody Map<String, Object> options) {
try {
return ResponseEntity.ok(ApiResponse.success(
tableManagementService.aggregateTableData(tableName, options == null ? Map.of() : options),
"테이블 집계를 성공적으로 조회했습니다."));
} catch (IllegalArgumentException e) {
return ResponseEntity.status(400).body(ApiResponse.error(e.getMessage()));
}
}
/** POST /api/table-management/tables/:tableName/aggregate-group
* body: { aggregation, groupBy, valueColumn?, filters?, limit?, orderDir? }
* → { rows: [{ group, value }, ...] }
*/
@PostMapping("/tables/{tableName}/aggregate-group")
public ResponseEntity<ApiResponse<Map<String, Object>>> aggregateTableGroup(
@PathVariable String tableName,
@RequestBody Map<String, Object> options) {
try {
return ResponseEntity.ok(ApiResponse.success(
tableManagementService.aggregateTableGroup(tableName, options == null ? Map.of() : options),
"테이블 그룹 집계를 성공적으로 조회했습니다."));
} catch (IllegalArgumentException e) {
return ResponseEntity.status(400).body(ApiResponse.error(e.getMessage()));
}
}
/** POST /api/table-management/tables/:tableName/select-rows
* body: { columns?, filters?, orderBy?, limit?, offset? }
* → { rows: [{...}, ...] }
*/
@PostMapping("/tables/{tableName}/select-rows")
public ResponseEntity<ApiResponse<Map<String, Object>>> selectTableRows(
@PathVariable String tableName,
@RequestBody Map<String, Object> options) {
try {
return ResponseEntity.ok(ApiResponse.success(
tableManagementService.selectTableRows(tableName, options == null ? Map.of() : options),
"테이블 row 를 성공적으로 조회했습니다."));
} catch (IllegalArgumentException e) {
return ResponseEntity.status(400).body(ApiResponse.error(e.getMessage()));
}
}
/** POST /api/table-management/tables/:tableName/record (단일 레코드) */
@PostMapping("/tables/{tableName}/record")
public ResponseEntity<ApiResponse<Map<String, Object>>> getTableRecord(
@PathVariable String tableName,
@RequestBody Map<String, Object> body) {
String filterColumn = (String) body.get("filter_column");
Object filterValue = body.get("filter_value");
String displayColumn = (String) body.get("display_column");
if (filterColumn == null || filterValue == null) {
return ResponseEntity.status(400).body(ApiResponse.error(
"tableName, filterColumn, filterValue가 필요합니다."));
}
Map<String, Object> options = new java.util.HashMap<>();
options.put("page", 1);
options.put("size", 1);
options.put("search", Map.of(filterColumn, filterValue.toString()));
Map<String, Object> result = tableManagementService.getTableData(tableName, options);
@SuppressWarnings("unchecked")
List<Map<String, Object>> data = (List<Map<String, Object>>) result.get("data");
if (data == null || data.isEmpty()) {
return ResponseEntity.status(404).body(ApiResponse.error("데이터를 찾을 수 없습니다."));
}
Map<String, Object> record = data.get(0);
Object displayValue = (displayColumn != null && !displayColumn.equals("*"))
? record.get(displayColumn) : record;
return ResponseEntity.ok(ApiResponse.success(
Map.of("value", displayValue != null ? displayValue : "", "record", record),
"레코드를 성공적으로 조회했습니다."));
}
/** POST /api/table-management/tables/:tableName/add */
@PostMapping("/tables/{tableName}/add")
public ResponseEntity<ApiResponse<Map<String, Object>>> addTableData(
@PathVariable String tableName,
@RequestBody Map<String, Object> data,
@RequestAttribute("role") String role,
@RequestAttribute("company_code") String companyCode) {
if (!isAdmin(role)) {
return ResponseEntity.status(403).body(ApiResponse.error("관리자 권한이 필요합니다."));
}
if (data == null || data.isEmpty()) {
return ResponseEntity.status(400).body(ApiResponse.error("추가할 데이터가 필요합니다."));
}
// 멀티테넌시: company_code 자동 추가
if (!data.containsKey("company_code") && tableManagementService.hasColumn(tableName, "company_code")) {
data.put("company_code", companyCode);
}
// 소프트 제약조건 검증
List<String> notNullViolations = tableManagementService.validateNotNullConstraints(tableName, data, companyCode);
if (!notNullViolations.isEmpty()) {
return ResponseEntity.status(400).body(ApiResponse.error(
"필수 항목이 비어있습니다: " + String.join(", ", notNullViolations)));
}
List<String> uniqueViolations = tableManagementService.validateUniqueConstraints(tableName, data, companyCode, null);
if (!uniqueViolations.isEmpty()) {
return ResponseEntity.status(400).body(ApiResponse.error(
"중복된 값이 존재합니다: " + String.join(", ", uniqueViolations)));
}
Map<String, Object> result = tableManagementService.addTableData(tableName, data);
return ResponseEntity.status(201).body(ApiResponse.success(
Map.of("id", result.getOrDefault("inserted_id", "")),
"테이블 데이터를 성공적으로 추가했습니다."));
}
/** PUT /api/table-management/tables/:tableName/edit */
@PutMapping("/tables/{tableName}/edit")
public ResponseEntity<ApiResponse<Void>> editTableData(
@PathVariable String tableName,
@RequestBody Map<String, Object> body,
@RequestAttribute("role") String role,
@RequestAttribute("company_code") String companyCode) {
if (!isAdmin(role)) {
return ResponseEntity.status(403).body(ApiResponse.error("관리자 권한이 필요합니다."));
}
@SuppressWarnings("unchecked")
Map<String, Object> originalData = (Map<String, Object>) body.get("original_data");
@SuppressWarnings("unchecked")
Map<String, Object> updatedData = (Map<String, Object>) body.get("updated_data");
if (originalData == null || updatedData == null) {
return ResponseEntity.status(400).body(ApiResponse.error("원본 데이터와 수정할 데이터가 모두 필요합니다."));
}
if (updatedData.isEmpty()) {
return ResponseEntity.status(400).body(ApiResponse.error("수정할 데이터가 없습니다."));
}
// 소프트 제약조건 검증
List<String> notNullViolations = tableManagementService.validateNotNullConstraints(tableName, updatedData, companyCode);
if (!notNullViolations.isEmpty()) {
return ResponseEntity.status(400).body(ApiResponse.error(
"필수 항목이 비어있습니다: " + String.join(", ", notNullViolations)));
}
String excludeId = originalData.get("id") != null ? originalData.get("id").toString() : null;
List<String> uniqueViolations = tableManagementService.validateUniqueConstraints(tableName, updatedData, companyCode, excludeId);
if (!uniqueViolations.isEmpty()) {
return ResponseEntity.status(400).body(ApiResponse.error(
"중복된 값이 존재합니다: " + String.join(", ", uniqueViolations)));
}
tableManagementService.editTableData(tableName, originalData, updatedData);
return ResponseEntity.ok(ApiResponse.success(null, "테이블 데이터를 성공적으로 수정했습니다."));
}
/** DELETE /api/table-management/tables/:tableName/delete */
@DeleteMapping("/tables/{tableName}/delete")
public ResponseEntity<ApiResponse<Void>> deleteTableData(
@PathVariable String tableName,
@RequestBody Object body,
@RequestAttribute("role") String role) {
if (!isAdmin(role)) {
return ResponseEntity.status(403).body(ApiResponse.error("관리자 권한이 필요합니다."));
}
List<Map<String, Object>> dataList;
if (body instanceof List) {
@SuppressWarnings("unchecked")
List<Map<String, Object>> cast = (List<Map<String, Object>>) body;
dataList = cast;
} else {
return ResponseEntity.status(400).body(ApiResponse.error("삭제할 데이터가 필요합니다."));
}
if (dataList.isEmpty()) {
return ResponseEntity.status(400).body(ApiResponse.error("삭제할 데이터가 필요합니다."));
}
tableManagementService.deleteTableData(tableName, dataList);
return ResponseEntity.ok(ApiResponse.success(null, "테이블 데이터를 성공적으로 삭제했습니다."));
}
// ──────────────────────────────────────────────────────────
// 로그 관리
// ──────────────────────────────────────────────────────────
/** POST /api/table-management/tables/:tableName/log */
@PostMapping("/tables/{tableName}/log")
public ResponseEntity<ApiResponse<Void>> createLogTable(
@PathVariable String tableName,
@RequestBody Map<String, Object> body,
@RequestAttribute("role") String role) {
if (!isSuperAdmin(role)) {
return ResponseEntity.status(403).body(ApiResponse.error("최고 관리자(SUPER_ADMIN) 권한이 필요합니다."));
}
@SuppressWarnings("unchecked")
List<String> logColumns = (List<String>) body.get("log_columns");
boolean isActive = Boolean.TRUE.equals(body.get("is_active"));
tableManagementService.createLogTable(tableName, logColumns, isActive);
return ResponseEntity.ok(ApiResponse.success(null, "로그 테이블이 생성되었습니다."));
}
/** GET /api/table-management/tables/:tableName/log/config */
@GetMapping("/tables/{tableName}/log/config")
public ResponseEntity<ApiResponse<Object>> getLogConfig(@PathVariable String tableName) {
Map<String, Object> config = tableManagementService.getLogConfig(tableName);
Object data = config != null ? config : Map.of();
return ResponseEntity.ok(ApiResponse.success(data));
}
/** GET /api/table-management/tables/:tableName/log */
@GetMapping("/tables/{tableName}/log")
public ResponseEntity<ApiResponse<Map<String, Object>>> getLogData(
@PathVariable String tableName,
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "50") int size) {
return ResponseEntity.ok(ApiResponse.success(
tableManagementService.getLogData(tableName, page, size)));
}
/** POST /api/table-management/tables/:tableName/log/toggle */
@PostMapping("/tables/{tableName}/log/toggle")
public ResponseEntity<ApiResponse<Void>> toggleLogTable(
@PathVariable String tableName,
@RequestBody Map<String, Object> body,
@RequestAttribute("role") String role) {
if (!isAdmin(role)) {
return ResponseEntity.status(403).body(ApiResponse.error("관리자 권한이 필요합니다."));
}
boolean isActive = Boolean.TRUE.equals(body.get("is_active"));
tableManagementService.toggleLogTable(tableName, isActive);
return ResponseEntity.ok(ApiResponse.success(null,
isActive ? "로그가 활성화되었습니다." : "로그가 비활성화되었습니다."));
}
// ──────────────────────────────────────────────────────────
// 카테고리 / 채번 컬럼
// ──────────────────────────────────────────────────────────
/** GET /api/table-management/category-columns */
@GetMapping("/category-columns")
public ResponseEntity<ApiResponse<List<Map<String, Object>>>> getCategoryColumnsByCompany(
@RequestAttribute("company_code") String companyCode) {
return ResponseEntity.ok(ApiResponse.success(
tableManagementService.getCategoryColumnsByCompany(companyCode)));
}
/** GET /api/table-management/numbering-columns */
@GetMapping("/numbering-columns")
public ResponseEntity<ApiResponse<List<Map<String, Object>>>> getNumberingColumnsByCompany(
@RequestAttribute("company_code") String companyCode) {
return ResponseEntity.ok(ApiResponse.success(
tableManagementService.getNumberingColumnsByCompany(companyCode)));
}
/** GET /api/table-management/menu/:menuObjid/category-columns */
@GetMapping("/menu/{menuObjid}/category-columns")
public ResponseEntity<ApiResponse<List<Map<String, Object>>>> getCategoryColumnsByMenu(
@PathVariable String menuObjid,
@RequestAttribute("company_code") String companyCode) {
return ResponseEntity.ok(ApiResponse.success(
tableManagementService.getCategoryColumnsByMenu(companyCode, menuObjid)));
}
// ──────────────────────────────────────────────────────────
// 현재 테이블을 참조하는 테이블
// ──────────────────────────────────────────────────────────
/** GET /api/table-management/columns/:tableName/referenced-by */
@GetMapping("/columns/{tableName}/referenced-by")
public ResponseEntity<ApiResponse<List<Map<String, Object>>>> getReferencedByTables(
@PathVariable String tableName,
@RequestAttribute("company_code") String companyCode) {
return ResponseEntity.ok(ApiResponse.success(
tableManagementService.getReferencedByTables(tableName, companyCode)));
}
// ──────────────────────────────────────────────────────────
// 다중 테이블 저장 / 엑셀 검증
// ──────────────────────────────────────────────────────────
/** POST /api/table-management/multi-table-save */
@PostMapping("/multi-table-save")
public ResponseEntity<ApiResponse<Map<String, Object>>> multiTableSave(
@RequestBody Map<String, Object> payload,
@RequestAttribute("role") String role,
@RequestAttribute("company_code") String companyCode) {
if (!isAdmin(role)) {
return ResponseEntity.status(403).body(ApiResponse.error("관리자 권한이 필요합니다."));
}
return ResponseEntity.ok(ApiResponse.success(
tableManagementService.multiTableSave(payload, companyCode),
"다중 테이블 저장이 완료되었습니다."));
}
/** POST /api/table-management/validate-excel */
@PostMapping("/validate-excel")
public ResponseEntity<ApiResponse<Map<String, Object>>> validateExcelData(
@RequestBody Map<String, Object> body,
@RequestAttribute("company_code") String companyCode) {
String tableName = (String) body.get("table_name");
@SuppressWarnings("unchecked")
List<Map<String, Object>> rows = (List<Map<String, Object>>) body.get("rows");
if (tableName == null || rows == null) {
return ResponseEntity.status(400).body(ApiResponse.error("tableName과 rows가 필요합니다."));
}
return ResponseEntity.ok(ApiResponse.success(
tableManagementService.validateExcelData(tableName, rows, companyCode)));
}
// ──────────────────────────────────────────────────────────
// DB 헬스체크
// ──────────────────────────────────────────────────────────
/** GET /api/table-management/health */
@GetMapping("/health")
public ResponseEntity<ApiResponse<Map<String, Object>>> checkDatabaseConnection() {
return ResponseEntity.ok(ApiResponse.success(
tableManagementService.checkDatabaseConnection(), "데이터베이스 연결 상태를 확인했습니다."));
}
// ──────────────────────────────────────────────────────────
// 권한 헬퍼
// ──────────────────────────────────────────────────────────
private boolean isAdmin(String role) {
return isSuperAdmin(role) || "COMPANY_ADMIN".equals(role);
}
private boolean isSuperAdmin(String roleOrCode) {
return "*".equals(roleOrCode) || "SUPER_ADMIN".equals(roleOrCode);
}
}