중간 세이브
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
package com.erp.controller;
|
||||
|
||||
import com.erp.dto.ApiResponse;
|
||||
import com.erp.service.BusinessRuleService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class BusinessRuleController {
|
||||
|
||||
private final BusinessRuleService businessRuleService;
|
||||
|
||||
/**
|
||||
* GET /api/dashboards/{dashboardId}/rules — 해당 대시보드의 룰 목록
|
||||
*/
|
||||
@GetMapping("/api/dashboards/{dashboardId}/rules")
|
||||
public ResponseEntity<ApiResponse<Map<String, Object>>> getBusinessRuleList(
|
||||
@PathVariable String dashboardId,
|
||||
@RequestAttribute("company_code") String companyCode,
|
||||
@RequestParam(required = false, defaultValue = "1") int page,
|
||||
@RequestParam(required = false, defaultValue = "100") int limit) {
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("dashboard_id", dashboardId);
|
||||
params.put("company_code", companyCode);
|
||||
params.put("page", page);
|
||||
params.put("limit", limit);
|
||||
return ResponseEntity.ok(ApiResponse.success(businessRuleService.getBusinessRuleList(params)));
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/rules/{ruleId} — 룰 상세 (노드 + 연결)
|
||||
*/
|
||||
@GetMapping("/api/rules/{ruleId}")
|
||||
public ResponseEntity<ApiResponse<Map<String, Object>>> getBusinessRuleInfo(
|
||||
@PathVariable String ruleId) {
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("rule_id", ruleId);
|
||||
Map<String, Object> result = businessRuleService.getBusinessRuleInfo(params);
|
||||
if (result == null) {
|
||||
return ResponseEntity.ok(ApiResponse.error("Rule not found"));
|
||||
}
|
||||
return ResponseEntity.ok(ApiResponse.success(result));
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /api/dashboards/{dashboardId}/rules — 룰 생성
|
||||
*/
|
||||
@PostMapping("/api/dashboards/{dashboardId}/rules")
|
||||
public ResponseEntity<ApiResponse<Map<String, Object>>> insertBusinessRule(
|
||||
@PathVariable String dashboardId,
|
||||
@RequestBody Map<String, Object> body,
|
||||
@RequestAttribute("company_code") String companyCode,
|
||||
@RequestAttribute("user_id") String userId) {
|
||||
body.put("dashboard_id", dashboardId);
|
||||
body.put("company_code", companyCode);
|
||||
body.put("user_id", userId);
|
||||
return ResponseEntity.ok(ApiResponse.success(businessRuleService.insertBusinessRule(body)));
|
||||
}
|
||||
|
||||
/**
|
||||
* PUT /api/rules/{ruleId} — 룰 수정
|
||||
*/
|
||||
@PutMapping("/api/rules/{ruleId}")
|
||||
public ResponseEntity<ApiResponse<Void>> updateBusinessRule(
|
||||
@PathVariable String ruleId,
|
||||
@RequestBody Map<String, Object> body,
|
||||
@RequestAttribute("user_id") String userId) {
|
||||
body.put("rule_id", ruleId);
|
||||
body.put("user_id", userId);
|
||||
businessRuleService.updateBusinessRule(body);
|
||||
return ResponseEntity.ok(ApiResponse.success(null));
|
||||
}
|
||||
|
||||
/**
|
||||
* DELETE /api/rules/{ruleId} — 룰 삭제
|
||||
*/
|
||||
@DeleteMapping("/api/rules/{ruleId}")
|
||||
public ResponseEntity<ApiResponse<Void>> deleteBusinessRule(
|
||||
@PathVariable String ruleId,
|
||||
@RequestAttribute("user_id") String userId) {
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("rule_id", ruleId);
|
||||
params.put("user_id", userId);
|
||||
businessRuleService.deleteBusinessRule(params);
|
||||
return ResponseEntity.ok(ApiResponse.success(null));
|
||||
}
|
||||
|
||||
/**
|
||||
* PUT /api/rules/{ruleId}/toggle — 활성/비활성 토글
|
||||
*/
|
||||
@PutMapping("/api/rules/{ruleId}/toggle")
|
||||
public ResponseEntity<ApiResponse<Void>> toggleBusinessRule(
|
||||
@PathVariable String ruleId,
|
||||
@RequestAttribute("user_id") String userId) {
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("rule_id", ruleId);
|
||||
params.put("user_id", userId);
|
||||
businessRuleService.toggleBusinessRule(params);
|
||||
return ResponseEntity.ok(ApiResponse.success(null));
|
||||
}
|
||||
}
|
||||
@@ -2,165 +2,137 @@ package com.erp.controller;
|
||||
|
||||
import com.erp.dto.ApiResponse;
|
||||
import com.erp.service.DashboardService;
|
||||
import com.erp.service.ExternalRestApiConnectionService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/dashboard")
|
||||
@RequestMapping("/api/dashboards")
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class DashboardController {
|
||||
|
||||
private final DashboardService dashboardService;
|
||||
private final ExternalRestApiConnectionService externalRestApiConnectionService;
|
||||
|
||||
// ═══ 대시보드 CRUD ═══
|
||||
|
||||
@GetMapping
|
||||
public ResponseEntity<ApiResponse<Map<String, Object>>> getDashboards(
|
||||
public ResponseEntity<ApiResponse<Map<String, Object>>> getDashboardList(
|
||||
@RequestAttribute("company_code") String companyCode,
|
||||
@RequestParam Map<String, Object> params) {
|
||||
@RequestAttribute("user_id") String userId,
|
||||
@RequestParam(required = false) String keyword,
|
||||
@RequestParam(defaultValue = "1") int page,
|
||||
@RequestParam(defaultValue = "50") int limit) {
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("company_code", companyCode);
|
||||
params.put("user_id", userId);
|
||||
params.put("keyword", keyword);
|
||||
params.put("page", page);
|
||||
params.put("limit", limit);
|
||||
return ResponseEntity.ok(ApiResponse.success(dashboardService.getDashboardList(params)));
|
||||
}
|
||||
|
||||
@GetMapping("/list")
|
||||
public ResponseEntity<ApiResponse<Map<String, Object>>> getDashboardsLegacy(
|
||||
@RequestAttribute("company_code") String companyCode,
|
||||
@RequestParam Map<String, Object> params) {
|
||||
params.put("company_code", companyCode);
|
||||
return ResponseEntity.ok(ApiResponse.success(dashboardService.getDashboardList(params)));
|
||||
}
|
||||
|
||||
@GetMapping("/public")
|
||||
public ResponseEntity<ApiResponse<Map<String, Object>>> getPublicDashboards(
|
||||
@RequestParam Map<String, Object> params) {
|
||||
params.put("company_code", "*");
|
||||
params.put("is_public", "true");
|
||||
return ResponseEntity.ok(ApiResponse.success(dashboardService.getDashboardList(params)));
|
||||
}
|
||||
|
||||
@GetMapping("/my")
|
||||
public ResponseEntity<ApiResponse<Map<String, Object>>> getMyDashboards(
|
||||
@RequestAttribute("company_code") String companyCode,
|
||||
@RequestParam Map<String, Object> params) {
|
||||
params.put("company_code", companyCode);
|
||||
return ResponseEntity.ok(ApiResponse.success(dashboardService.getDashboardList(params)));
|
||||
}
|
||||
|
||||
@GetMapping("/public/{id}")
|
||||
public ResponseEntity<ApiResponse<Map<String, Object>>> getPublicDashboard(
|
||||
@PathVariable String id) {
|
||||
Map<String, Object> result = dashboardService.getDashboardById(id, null);
|
||||
if (result == null) return ResponseEntity.status(404).body(ApiResponse.error("대시보드를 찾을 수 없습니다."));
|
||||
return ResponseEntity.ok(ApiResponse.success(result));
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public ResponseEntity<ApiResponse<Map<String, Object>>> getDashboard(
|
||||
@RequestAttribute("company_code") String companyCode,
|
||||
@RequestAttribute(value = "user_id", required = false) String userId,
|
||||
@PathVariable String id) {
|
||||
Map<String, Object> result = dashboardService.getDashboardById(id, companyCode);
|
||||
if (result == null) return ResponseEntity.status(404).body(ApiResponse.error("대시보드를 찾을 수 없거나 접근 권한이 없습니다."));
|
||||
// 다른 사람 것 조회 시 조회수 증가
|
||||
if (userId != null && !userId.equals(result.get("created_by"))) {
|
||||
dashboardService.incrementViewCount(id);
|
||||
@GetMapping("/{dashboardId}")
|
||||
public ResponseEntity<ApiResponse<Map<String, Object>>> getDashboardInfo(
|
||||
@PathVariable String dashboardId) {
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("dashboard_id", dashboardId);
|
||||
Map<String, Object> result = dashboardService.getDashboardInfo(params);
|
||||
if (result == null) {
|
||||
return ResponseEntity.ok(ApiResponse.error("대시보드를 찾을 수 없습니다"));
|
||||
}
|
||||
return ResponseEntity.ok(ApiResponse.success(result));
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public ResponseEntity<ApiResponse<Map<String, Object>>> createDashboard(
|
||||
public ResponseEntity<ApiResponse<Map<String, Object>>> insertDashboard(
|
||||
@RequestAttribute("company_code") String companyCode,
|
||||
@RequestAttribute(value = "user_id", required = false) String userId,
|
||||
@RequestAttribute("user_id") String userId,
|
||||
@RequestBody Map<String, Object> body) {
|
||||
String title = (String) body.get("title");
|
||||
if (title == null || title.isBlank()) return ResponseEntity.status(400).body(ApiResponse.error("대시보드 제목이 필요합니다."));
|
||||
Map<String, Object> result = dashboardService.createDashboard(body, userId, companyCode);
|
||||
return ResponseEntity.status(201).body(ApiResponse.success(result, "대시보드가 생성되었습니다."));
|
||||
body.put("company_code", companyCode);
|
||||
body.put("user_id", userId);
|
||||
return ResponseEntity.ok(ApiResponse.success(dashboardService.insertDashboard(body)));
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public ResponseEntity<ApiResponse<Map<String, Object>>> updateDashboard(
|
||||
@PathVariable String id,
|
||||
@RequestAttribute(value = "user_id", required = false) String userId,
|
||||
@PutMapping("/{dashboardId}")
|
||||
public ResponseEntity<ApiResponse<Void>> updateDashboard(
|
||||
@PathVariable String dashboardId,
|
||||
@RequestAttribute("user_id") String userId,
|
||||
@RequestBody Map<String, Object> body) {
|
||||
Map<String, Object> result = dashboardService.updateDashboard(id, body, userId);
|
||||
if (result == null) return ResponseEntity.status(404).body(ApiResponse.error("대시보드를 찾을 수 없거나 수정 권한이 없습니다."));
|
||||
return ResponseEntity.ok(ApiResponse.success(result, "대시보드가 수정되었습니다."));
|
||||
body.put("dashboard_id", dashboardId);
|
||||
body.put("user_id", userId);
|
||||
dashboardService.updateDashboard(body);
|
||||
return ResponseEntity.ok(ApiResponse.success(null, "수정 완료"));
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public ResponseEntity<ApiResponse<Map<String, Object>>> deleteDashboard(
|
||||
@PathVariable String id,
|
||||
@RequestAttribute(value = "user_id", required = false) String userId) {
|
||||
boolean deleted = dashboardService.deleteDashboard(id, userId);
|
||||
if (!deleted) return ResponseEntity.status(404).body(ApiResponse.error("대시보드를 찾을 수 없거나 삭제 권한이 없습니다."));
|
||||
return ResponseEntity.ok(ApiResponse.success(null, "대시보드가 삭제되었습니다."));
|
||||
@DeleteMapping("/{dashboardId}")
|
||||
public ResponseEntity<ApiResponse<Void>> deleteDashboard(
|
||||
@PathVariable String dashboardId,
|
||||
@RequestAttribute("user_id") String userId) {
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("dashboard_id", dashboardId);
|
||||
params.put("user_id", userId);
|
||||
dashboardService.deleteDashboard(params);
|
||||
return ResponseEntity.ok(ApiResponse.success(null, "삭제 완료"));
|
||||
}
|
||||
|
||||
@PostMapping("/execute-query")
|
||||
public ResponseEntity<ApiResponse<Map<String, Object>>> executeQuery(
|
||||
// ═══ 카드 CRUD ═══
|
||||
|
||||
@GetMapping("/{dashboardId}/cards")
|
||||
public ResponseEntity<ApiResponse<List<Map<String, Object>>>> getDashboardCards(
|
||||
@PathVariable String dashboardId) {
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("dashboard_id", dashboardId);
|
||||
return ResponseEntity.ok(ApiResponse.success(dashboardService.getDashboardCardList(params)));
|
||||
}
|
||||
|
||||
@PostMapping("/{dashboardId}/cards")
|
||||
public ResponseEntity<ApiResponse<Map<String, Object>>> insertDashboardCard(
|
||||
@PathVariable String dashboardId,
|
||||
@RequestBody Map<String, Object> body) {
|
||||
String query = (String) body.get("query");
|
||||
if (query == null || query.isBlank()) return ResponseEntity.status(400).body(ApiResponse.error("쿼리가 필요합니다."));
|
||||
try {
|
||||
Map<String, Object> result = dashboardService.executeQuery(query);
|
||||
return ResponseEntity.ok(ApiResponse.success(result, "쿼리가 실행되었습니다."));
|
||||
} catch (IllegalArgumentException e) {
|
||||
return ResponseEntity.status(400).body(ApiResponse.error(e.getMessage()));
|
||||
}
|
||||
body.put("dashboard_id", dashboardId);
|
||||
return ResponseEntity.ok(ApiResponse.success(dashboardService.insertDashboardCard(body)));
|
||||
}
|
||||
|
||||
@PostMapping("/execute-dml")
|
||||
public ResponseEntity<ApiResponse<Map<String, Object>>> executeDml(
|
||||
@PutMapping("/{dashboardId}/cards/{cardId}")
|
||||
public ResponseEntity<ApiResponse<Void>> updateDashboardCard(
|
||||
@PathVariable String dashboardId,
|
||||
@PathVariable String cardId,
|
||||
@RequestBody Map<String, Object> body) {
|
||||
String query = (String) body.get("query");
|
||||
if (query == null || query.isBlank()) return ResponseEntity.status(400).body(ApiResponse.error("쿼리가 필요합니다."));
|
||||
try {
|
||||
Map<String, Object> result = dashboardService.executeDml(query);
|
||||
return ResponseEntity.ok(ApiResponse.success(result, "쿼리가 실행되었습니다."));
|
||||
} catch (IllegalArgumentException e) {
|
||||
return ResponseEntity.status(400).body(ApiResponse.error(e.getMessage()));
|
||||
}
|
||||
body.put("dashboard_id", dashboardId);
|
||||
body.put("card_id", cardId);
|
||||
dashboardService.updateDashboardCard(body);
|
||||
return ResponseEntity.ok(ApiResponse.success(null));
|
||||
}
|
||||
|
||||
@PostMapping("/fetch-external-api")
|
||||
public ResponseEntity<ApiResponse<Object>> fetchExternalApi(
|
||||
@DeleteMapping("/{dashboardId}/cards/{cardId}")
|
||||
public ResponseEntity<ApiResponse<Void>> deleteDashboardCard(
|
||||
@PathVariable String dashboardId,
|
||||
@PathVariable String cardId) {
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("card_id", cardId);
|
||||
dashboardService.deleteDashboardCard(params);
|
||||
return ResponseEntity.ok(ApiResponse.success(null));
|
||||
}
|
||||
|
||||
@PutMapping("/{dashboardId}/cards/batch")
|
||||
public ResponseEntity<ApiResponse<Void>> updateCardPositions(
|
||||
@PathVariable String dashboardId,
|
||||
@RequestBody Map<String, Object> body) {
|
||||
dashboardService.updateCardPositions(body);
|
||||
return ResponseEntity.ok(ApiResponse.success(null, "일괄 업데이트 완료"));
|
||||
}
|
||||
|
||||
// ═══ 사이드바 메뉴 ═══
|
||||
|
||||
@GetMapping("/sidebar/menu")
|
||||
public ResponseEntity<ApiResponse<List<Map<String, Object>>>> getSidebarMenu(
|
||||
@RequestAttribute("company_code") String companyCode,
|
||||
@RequestBody Map<String, Object> body) {
|
||||
String url = (String) body.get("url");
|
||||
if (url == null || url.isBlank()) return ResponseEntity.status(400).body(ApiResponse.error("URL이 필요합니다."));
|
||||
Object externalConnectionId = body.get("external_connection_id");
|
||||
|
||||
Map<String, Object> requestParams = new HashMap<>(body);
|
||||
requestParams.put("company_code", companyCode);
|
||||
if (externalConnectionId != null) {
|
||||
int connId = Integer.parseInt(String.valueOf(externalConnectionId));
|
||||
Map<String, Object> result = externalRestApiConnectionService.fetchData(connId, url, null, requestParams);
|
||||
return ResponseEntity.ok(ApiResponse.success(result));
|
||||
}
|
||||
|
||||
// 커넥션 없이 직접 호출 - 기본 응답
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
result.put("url", url);
|
||||
result.put("message", "externalConnectionId가 필요합니다.");
|
||||
return ResponseEntity.ok(ApiResponse.success(result));
|
||||
}
|
||||
|
||||
@PostMapping("/table-schema")
|
||||
public ResponseEntity<ApiResponse<Map<String, Object>>> getTableSchema(
|
||||
@RequestBody Map<String, Object> body) {
|
||||
String tableName = (String) body.get("table_name");
|
||||
if (tableName == null || tableName.isBlank()) return ResponseEntity.status(400).body(ApiResponse.error("테이블명이 필요합니다."));
|
||||
try {
|
||||
Map<String, Object> result = dashboardService.getTableSchema(tableName);
|
||||
return ResponseEntity.ok(ApiResponse.success(result));
|
||||
} catch (IllegalArgumentException e) {
|
||||
return ResponseEntity.status(400).body(ApiResponse.error(e.getMessage()));
|
||||
}
|
||||
@RequestAttribute("user_id") String userId) {
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("company_code", companyCode);
|
||||
params.put("user_id", userId);
|
||||
return ResponseEntity.ok(ApiResponse.success(dashboardService.getSidebarMenu(params)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.erp.controller;
|
||||
|
||||
import com.erp.dto.ApiResponse;
|
||||
import com.erp.service.MetaService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/meta")
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class MetaController {
|
||||
|
||||
private final MetaService metaService;
|
||||
|
||||
/**
|
||||
* GET /api/meta/tables — 접근 가능한 테이블 목록
|
||||
*/
|
||||
@GetMapping("/tables")
|
||||
public ResponseEntity<ApiResponse<List<Map<String, Object>>>> getMetaTableList(
|
||||
@RequestAttribute("company_code") String companyCode) {
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("company_code", companyCode);
|
||||
return ResponseEntity.ok(ApiResponse.success(metaService.getMetaTableList(params)));
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/meta/tables/{tableName}/fields — 특정 테이블의 FieldConfig[] 반환
|
||||
*/
|
||||
@GetMapping("/tables/{tableName}/fields")
|
||||
public ResponseEntity<ApiResponse<Map<String, Object>>> getMetaFields(
|
||||
@PathVariable String tableName,
|
||||
@RequestAttribute("company_code") String companyCode) {
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("table_name", tableName);
|
||||
params.put("company_code", companyCode);
|
||||
return ResponseEntity.ok(ApiResponse.success(metaService.getMetaFields(params)));
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/meta/tables/{tableName}/relations — 테이블 간 업무 관계 (Phase 5 제어 모드)
|
||||
*/
|
||||
@GetMapping("/tables/{tableName}/relations")
|
||||
public ResponseEntity<ApiResponse<List<Map<String, Object>>>> getMetaRelations(
|
||||
@PathVariable String tableName,
|
||||
@RequestAttribute("company_code") String companyCode) {
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("table_name", tableName);
|
||||
params.put("company_code", companyCode);
|
||||
return ResponseEntity.ok(ApiResponse.success(metaService.getMetaRelations(params)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package com.erp.controller;
|
||||
|
||||
import com.erp.dto.ApiResponse;
|
||||
import com.erp.service.TemplateService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/templates")
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class TemplateController {
|
||||
|
||||
private final TemplateService templateService;
|
||||
|
||||
/**
|
||||
* GET /api/templates — 템플릿 목록
|
||||
*/
|
||||
@GetMapping
|
||||
public ResponseEntity<ApiResponse<Map<String, Object>>> getTemplateList(
|
||||
@RequestAttribute("company_code") String companyCode,
|
||||
@RequestParam Map<String, Object> params) {
|
||||
params.putIfAbsent("company_code", companyCode);
|
||||
return ResponseEntity.ok(ApiResponse.success(templateService.getTemplateList(params)));
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/templates/{templateId} — 템플릿 상세
|
||||
*/
|
||||
@GetMapping("/{templateId}")
|
||||
public ResponseEntity<ApiResponse<Map<String, Object>>> getTemplateInfo(
|
||||
@PathVariable String templateId,
|
||||
@RequestAttribute("company_code") String companyCode) {
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("template_id", templateId);
|
||||
params.put("company_code", companyCode);
|
||||
Map<String, Object> result = templateService.getTemplateInfo(params);
|
||||
if (result == null) {
|
||||
return ResponseEntity.ok(ApiResponse.error("템플릿을 찾을 수 없습니다"));
|
||||
}
|
||||
return ResponseEntity.ok(ApiResponse.success(result));
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /api/templates — 템플릿 생성
|
||||
*/
|
||||
@PostMapping
|
||||
public ResponseEntity<ApiResponse<Map<String, Object>>> insertTemplate(
|
||||
@RequestBody Map<String, Object> params,
|
||||
@RequestAttribute("company_code") String companyCode,
|
||||
@RequestAttribute("user_id") String userId) {
|
||||
params.put("company_code", companyCode);
|
||||
params.put("user_id", userId);
|
||||
Map<String, Object> result = templateService.insertTemplate(params);
|
||||
return ResponseEntity.ok(ApiResponse.success(result));
|
||||
}
|
||||
|
||||
/**
|
||||
* PUT /api/templates/{templateId} — 템플릿 수정
|
||||
*/
|
||||
@PutMapping("/{templateId}")
|
||||
public ResponseEntity<ApiResponse<Void>> updateTemplate(
|
||||
@PathVariable String templateId,
|
||||
@RequestBody Map<String, Object> params,
|
||||
@RequestAttribute("user_id") String userId) {
|
||||
params.put("template_id", templateId);
|
||||
params.put("user_id", userId);
|
||||
templateService.updateTemplate(params);
|
||||
return ResponseEntity.ok(ApiResponse.success(null));
|
||||
}
|
||||
|
||||
/**
|
||||
* PUT /api/templates/{templateId}/publish — 템플릿 게시
|
||||
*/
|
||||
@PutMapping("/{templateId}/publish")
|
||||
public ResponseEntity<ApiResponse<Void>> publishTemplate(
|
||||
@PathVariable String templateId,
|
||||
@RequestAttribute("user_id") String userId) {
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("template_id", templateId);
|
||||
params.put("user_id", userId);
|
||||
templateService.publishTemplate(params);
|
||||
return ResponseEntity.ok(ApiResponse.success(null));
|
||||
}
|
||||
|
||||
/**
|
||||
* DELETE /api/templates/{templateId} — 템플릿 삭제 (소프트)
|
||||
*/
|
||||
@DeleteMapping("/{templateId}")
|
||||
public ResponseEntity<ApiResponse<Void>> deleteTemplate(
|
||||
@PathVariable String templateId,
|
||||
@RequestAttribute("user_id") String userId) {
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("template_id", templateId);
|
||||
params.put("user_id", userId);
|
||||
templateService.deleteTemplate(params);
|
||||
return ResponseEntity.ok(ApiResponse.success(null));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.erp.controller;
|
||||
|
||||
import com.erp.dto.ApiResponse;
|
||||
import com.erp.service.UserOverrideService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/overrides")
|
||||
@RequiredArgsConstructor
|
||||
public class UserOverrideController {
|
||||
|
||||
private final UserOverrideService userOverrideService;
|
||||
|
||||
@GetMapping
|
||||
public ResponseEntity<ApiResponse<Map<String, Object>>> getUserOverride(
|
||||
@RequestAttribute("user_id") String userId,
|
||||
@RequestParam String card_id) {
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("user_id", userId);
|
||||
params.put("card_id", card_id);
|
||||
Map<String, Object> result = userOverrideService.getUserOverride(params);
|
||||
return ResponseEntity.ok(ApiResponse.success(result));
|
||||
}
|
||||
|
||||
@PutMapping
|
||||
public ResponseEntity<ApiResponse<Void>> upsertUserOverride(
|
||||
@RequestAttribute("user_id") String userId,
|
||||
@RequestBody Map<String, Object> body) {
|
||||
body.put("user_id", userId);
|
||||
userOverrideService.upsertUserOverride(body);
|
||||
return ResponseEntity.ok(ApiResponse.success(null, "저장 완료"));
|
||||
}
|
||||
|
||||
@DeleteMapping
|
||||
public ResponseEntity<ApiResponse<Void>> deleteUserOverride(
|
||||
@RequestAttribute("user_id") String userId,
|
||||
@RequestParam String card_id) {
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("user_id", userId);
|
||||
params.put("card_id", card_id);
|
||||
userOverrideService.deleteUserOverride(params);
|
||||
return ResponseEntity.ok(ApiResponse.success(null, "삭제 완료"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package com.erp.service;
|
||||
|
||||
import com.erp.common.BaseService;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
public class BusinessRuleService extends BaseService {
|
||||
|
||||
@Autowired
|
||||
private CommonService commonService;
|
||||
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
private static final String NS = "businessRule.";
|
||||
|
||||
public Map<String, Object> getBusinessRuleList(Map<String, Object> params) {
|
||||
commonService.applyPagination(params);
|
||||
int totalCount = sqlSession.selectOne(NS + "getBusinessRuleListCnt", params);
|
||||
List<Map<String, Object>> list = sqlSession.selectList(NS + "getBusinessRuleList", params);
|
||||
return commonService.buildListResponse(list, totalCount, params);
|
||||
}
|
||||
|
||||
public Map<String, Object> getBusinessRuleInfo(Map<String, Object> params) {
|
||||
Map<String, Object> row = sqlSession.selectOne(NS + "getBusinessRuleInfo", params);
|
||||
if (row != null) {
|
||||
parseJsonField(row, "nodes");
|
||||
parseJsonField(row, "connections");
|
||||
}
|
||||
return row;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Map<String, Object> insertBusinessRule(Map<String, Object> params) {
|
||||
String ruleId = "rule_" + UUID.randomUUID().toString().replace("-", "").substring(0, 12);
|
||||
params.put("rule_id", ruleId);
|
||||
stringifyJsonField(params, "nodes");
|
||||
stringifyJsonField(params, "connections");
|
||||
sqlSession.insert(NS + "insertBusinessRule", params);
|
||||
return Map.of("rule_id", ruleId);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void updateBusinessRule(Map<String, Object> params) {
|
||||
stringifyJsonField(params, "nodes");
|
||||
stringifyJsonField(params, "connections");
|
||||
sqlSession.update(NS + "updateBusinessRule", params);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public int deleteBusinessRule(Map<String, Object> params) {
|
||||
return sqlSession.update(NS + "deleteBusinessRule", params);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void toggleBusinessRule(Map<String, Object> params) {
|
||||
sqlSession.update(NS + "toggleBusinessRule", params);
|
||||
}
|
||||
|
||||
// ── JSONB 유틸 ──
|
||||
|
||||
private void parseJsonField(Map<String, Object> row, String key) {
|
||||
Object val = row.get(key);
|
||||
if (val instanceof String) {
|
||||
try {
|
||||
row.put(key, objectMapper.readValue((String) val, Object.class));
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to parse JSONB field '{}': {}", key, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void stringifyJsonField(Map<String, Object> params, String key) {
|
||||
Object val = params.get(key);
|
||||
if (val != null && !(val instanceof String)) {
|
||||
try {
|
||||
params.put(key, objectMapper.writeValueAsString(val));
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to stringify field '{}': {}", key, e.getMessage());
|
||||
params.put(key, "[]");
|
||||
}
|
||||
}
|
||||
if (val == null) {
|
||||
params.put(key, "[]");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,347 +1,106 @@
|
||||
package com.erp.service;
|
||||
|
||||
import com.erp.common.BaseService;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
public class DashboardService extends BaseService {
|
||||
|
||||
@Autowired private JdbcTemplate jdbcTemplate;
|
||||
@Autowired private ObjectMapper objectMapper;
|
||||
@Autowired
|
||||
private CommonService commonService;
|
||||
|
||||
private static final String NS = "dashboard.";
|
||||
|
||||
// ═══ 대시보드 CRUD ═══
|
||||
|
||||
// ── 목록 조회 ────────────────────────────────────────────────────────────────
|
||||
public Map<String, Object> getDashboardList(Map<String, Object> params) {
|
||||
String companyCode = (String) params.getOrDefault("company_code", "*");
|
||||
String search = (String) params.get("search");
|
||||
String category = (String) params.get("category");
|
||||
int page = toInt(params.get("page"), 1);
|
||||
int limit = Math.min(toInt(params.get("limit"), 20), 100);
|
||||
int offset = (page - 1) * limit;
|
||||
|
||||
List<Object> args = new ArrayList<>();
|
||||
StringBuilder where = new StringBuilder("d.deleted_date IS NULL");
|
||||
|
||||
if (!"*".equals(companyCode)) {
|
||||
where.append(" AND d.company_code = ?");
|
||||
args.add(companyCode);
|
||||
}
|
||||
if (search != null && !search.isBlank()) {
|
||||
where.append(" AND (d.title ILIKE ? OR d.description ILIKE ?)");
|
||||
args.add("%" + search + "%");
|
||||
args.add("%" + search + "%");
|
||||
}
|
||||
if (category != null && !category.isBlank()) {
|
||||
where.append(" AND d.category = ?");
|
||||
args.add(category);
|
||||
}
|
||||
|
||||
String countSql = "SELECT COUNT(DISTINCT d.id) FROM dashboards d WHERE " + where;
|
||||
int total = jdbcTemplate.queryForObject(countSql, Integer.class, args.toArray());
|
||||
|
||||
String listSql = "SELECT d.id, d.title, d.description, d.thumbnail_url, d.is_public," +
|
||||
" d.created_by, d.created_date, d.updated_date, d.tags, d.category, d.view_count, d.company_code," +
|
||||
" u.user_name as created_by_name," +
|
||||
" COUNT(de.id) as elements_count" +
|
||||
" FROM dashboards d" +
|
||||
" LEFT JOIN dashboard_elements de ON d.id = de.dashboard_id" +
|
||||
" LEFT JOIN user_info u ON d.created_by = u.user_id" +
|
||||
" WHERE " + where +
|
||||
" GROUP BY d.id, d.title, d.description, d.thumbnail_url, d.is_public," +
|
||||
" d.created_by, d.created_date, d.updated_date, d.tags, d.category, d.view_count, d.company_code, u.user_name" +
|
||||
" ORDER BY d.updated_date DESC LIMIT ? OFFSET ?";
|
||||
List<Object> listArgs = new ArrayList<>(args);
|
||||
listArgs.add(limit);
|
||||
listArgs.add(offset);
|
||||
|
||||
List<Map<String, Object>> rows = jdbcTemplate.queryForList(listSql, listArgs.toArray());
|
||||
List<Map<String, Object>> dashboards = new ArrayList<>();
|
||||
for (Map<String, Object> row : rows) {
|
||||
dashboards.add(formatDashboardRow(row, false));
|
||||
}
|
||||
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
result.put("dashboards", dashboards);
|
||||
Map<String, Object> pagination = new LinkedHashMap<>();
|
||||
pagination.put("page", page);
|
||||
pagination.put("limit", limit);
|
||||
pagination.put("total", total);
|
||||
pagination.put("total_pages", (int) Math.ceil((double) total / limit));
|
||||
result.put("pagination", pagination);
|
||||
return result;
|
||||
commonService.applyPagination(params);
|
||||
int totalCount = sqlSession.selectOne(NS + "getDashboardListCnt", params);
|
||||
List<Map<String, Object>> list = sqlSession.selectList(NS + "getDashboardList", params);
|
||||
return commonService.buildListResponse(list, totalCount, params);
|
||||
}
|
||||
|
||||
// ── 단건 조회 (요소 포함) ─────────────────────────────────────────────────────
|
||||
public Map<String, Object> getDashboardById(String dashboardId, String companyCode) {
|
||||
List<Object> args = new ArrayList<>();
|
||||
args.add(dashboardId);
|
||||
String where = "id = ? AND deleted_date IS NULL";
|
||||
if (companyCode != null && !"*".equals(companyCode)) {
|
||||
where += " AND company_code = ?";
|
||||
args.add(companyCode);
|
||||
}
|
||||
|
||||
List<Map<String, Object>> rows = jdbcTemplate.queryForList(
|
||||
"SELECT * FROM dashboards WHERE " + where, args.toArray());
|
||||
if (rows.isEmpty()) return null;
|
||||
|
||||
Map<String, Object> dashboard = formatDashboardRow(rows.get(0), true);
|
||||
|
||||
List<Map<String, Object>> elements = jdbcTemplate.queryForList(
|
||||
"SELECT * FROM dashboard_elements WHERE dashboard_id = ? ORDER BY display_order ASC",
|
||||
dashboardId);
|
||||
dashboard.put("elements", elements.stream().map(this::formatElement).collect(java.util.stream.Collectors.toList()));
|
||||
return dashboard;
|
||||
public Map<String, Object> getDashboardInfo(Map<String, Object> params) {
|
||||
return sqlSession.selectOne(NS + "getDashboardInfo", params);
|
||||
}
|
||||
|
||||
// ── 생성 ─────────────────────────────────────────────────────────────────────
|
||||
@Transactional
|
||||
public Map<String, Object> createDashboard(Map<String, Object> params, String userId, String companyCode) {
|
||||
String dashboardId = UUID.randomUUID().toString();
|
||||
String title = String.valueOf(params.get("title"));
|
||||
String description = (String) params.get("description");
|
||||
boolean isPublic = Boolean.TRUE.equals(params.get("is_public")) || "true".equals(String.valueOf(params.get("is_public")));
|
||||
String tagsJson = toJson(params.getOrDefault("tags", new ArrayList<>()));
|
||||
String category = (String) params.get("category");
|
||||
String settingsJson = toJson(params.getOrDefault("settings", new HashMap<>()));
|
||||
|
||||
jdbcTemplate.update(
|
||||
"INSERT INTO dashboards (id, title, description, is_public, created_by, created_date, updated_date, tags, category, view_count, settings, company_code)" +
|
||||
" VALUES (?, ?, ?, ?, ?, NOW(), NOW(), ?::jsonb, ?, 0, ?::jsonb, ?)",
|
||||
dashboardId, title, description, isPublic, userId, tagsJson, category, settingsJson, companyCode != null ? companyCode : "DEFAULT");
|
||||
|
||||
insertElements(dashboardId, params.get("elements"));
|
||||
return getDashboardById(dashboardId, "*");
|
||||
public Map<String, Object> insertDashboard(Map<String, Object> params) {
|
||||
String dashboardId = "dash_" + UUID.randomUUID().toString().replace("-", "").substring(0, 12);
|
||||
params.put("dashboard_id", dashboardId);
|
||||
if (params.get("icon") == null) {
|
||||
params.put("icon", "\uD83D\uDCCB");
|
||||
}
|
||||
if (params.get("display_order") == null) {
|
||||
params.put("display_order", 0);
|
||||
}
|
||||
sqlSession.insert(NS + "insertDashboard", params);
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("dashboard_id", dashboardId);
|
||||
return result;
|
||||
}
|
||||
|
||||
// ── 수정 ─────────────────────────────────────────────────────────────────────
|
||||
@Transactional
|
||||
public Map<String, Object> updateDashboard(String dashboardId, Map<String, Object> params, String userId) {
|
||||
List<String> sets = new ArrayList<>();
|
||||
List<Object> args = new ArrayList<>();
|
||||
|
||||
if (params.containsKey("title")) { sets.add("title = ?"); args.add(params.get("title")); }
|
||||
if (params.containsKey("description")) { sets.add("description = ?"); args.add(params.get("description")); }
|
||||
if (params.containsKey("is_public")) {
|
||||
sets.add("is_public = ?");
|
||||
args.add(Boolean.TRUE.equals(params.get("is_public")) || "true".equals(String.valueOf(params.get("is_public"))));
|
||||
}
|
||||
if (params.containsKey("tags")) { sets.add("tags = ?::jsonb"); args.add(toJson(params.get("tags"))); }
|
||||
if (params.containsKey("category")) { sets.add("category = ?"); args.add(params.get("category")); }
|
||||
if (params.containsKey("settings")) { sets.add("settings = ?::jsonb"); args.add(toJson(params.get("settings"))); }
|
||||
sets.add("updated_date = NOW()");
|
||||
|
||||
args.add(dashboardId);
|
||||
args.add(userId);
|
||||
|
||||
int updated = jdbcTemplate.update(
|
||||
"UPDATE dashboards SET " + String.join(", ", sets) +
|
||||
" WHERE id = ? AND created_by = ? AND deleted_date IS NULL",
|
||||
args.toArray());
|
||||
if (updated == 0) return null;
|
||||
|
||||
if (params.containsKey("elements")) {
|
||||
jdbcTemplate.update("DELETE FROM dashboard_elements WHERE dashboard_id = ?", dashboardId);
|
||||
insertElements(dashboardId, params.get("elements"));
|
||||
}
|
||||
return getDashboardById(dashboardId, "*");
|
||||
public void updateDashboard(Map<String, Object> params) {
|
||||
sqlSession.update(NS + "updateDashboard", params);
|
||||
}
|
||||
|
||||
// ── 삭제 (소프트) ────────────────────────────────────────────────────────────
|
||||
@Transactional
|
||||
public boolean deleteDashboard(String dashboardId, String userId) {
|
||||
int deleted = jdbcTemplate.update(
|
||||
"UPDATE dashboards SET deleted_date = NOW(), updated_date = NOW() WHERE id = ? AND created_by = ? AND deleted_date IS NULL",
|
||||
dashboardId, userId);
|
||||
return deleted > 0;
|
||||
public int deleteDashboard(Map<String, Object> params) {
|
||||
return sqlSession.update(NS + "deleteDashboard", params);
|
||||
}
|
||||
|
||||
// ── 조회수 증가 ───────────────────────────────────────────────────────────────
|
||||
public void incrementViewCount(String dashboardId) {
|
||||
jdbcTemplate.update("UPDATE dashboards SET view_count = view_count + 1 WHERE id = ? AND deleted_date IS NULL", dashboardId);
|
||||
// ═══ 카드 CRUD ═══
|
||||
|
||||
public List<Map<String, Object>> getDashboardCardList(Map<String, Object> params) {
|
||||
return sqlSession.selectList(NS + "getDashboardCardList", params);
|
||||
}
|
||||
|
||||
// ── execute-query (SELECT만) ──────────────────────────────────────────────────
|
||||
public Map<String, Object> executeQuery(String sql) {
|
||||
String trimmed = sql.trim();
|
||||
String lower = trimmed.toLowerCase();
|
||||
if (!lower.startsWith("select") && !lower.startsWith("with")) {
|
||||
throw new IllegalArgumentException("SELECT 또는 WITH 쿼리만 허용됩니다.");
|
||||
}
|
||||
List<Map<String, Object>> rows = jdbcTemplate.queryForList(trimmed);
|
||||
List<String> columns = rows.isEmpty() ? new ArrayList<>() : new ArrayList<>(rows.get(0).keySet());
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
result.put("columns", columns);
|
||||
result.put("rows", rows);
|
||||
result.put("row_count", rows.size());
|
||||
@Transactional
|
||||
public Map<String, Object> insertDashboardCard(Map<String, Object> params) {
|
||||
String cardId = "card_" + UUID.randomUUID().toString().replace("-", "").substring(0, 12);
|
||||
params.put("card_id", cardId);
|
||||
if (params.get("position_x") == null) params.put("position_x", 50);
|
||||
if (params.get("position_y") == null) params.put("position_y", 50);
|
||||
if (params.get("width") == null) params.put("width", 600);
|
||||
if (params.get("height") == null) params.put("height", 400);
|
||||
if (params.get("display_order") == null) params.put("display_order", 0);
|
||||
sqlSession.insert(NS + "insertDashboardCard", params);
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("card_id", cardId);
|
||||
return result;
|
||||
}
|
||||
|
||||
// ── execute-dml (INSERT/UPDATE/DELETE) ────────────────────────────────────────
|
||||
public Map<String, Object> executeDml(String sql) {
|
||||
String trimmed = sql.trim();
|
||||
String lower = trimmed.toLowerCase();
|
||||
boolean isAllowed = lower.startsWith("insert") || lower.startsWith("update") || lower.startsWith("delete");
|
||||
if (!isAllowed) throw new IllegalArgumentException("INSERT, UPDATE, DELETE 쿼리만 허용됩니다.");
|
||||
String[] dangerous = {"drop table", "drop database", "truncate", "alter table", "create table"};
|
||||
for (String d : dangerous) {
|
||||
if (lower.contains(d)) throw new IllegalArgumentException("허용되지 않는 쿼리입니다.");
|
||||
}
|
||||
String command = trimmed.toUpperCase().split("\\s+")[0];
|
||||
int rowCount = jdbcTemplate.update(trimmed);
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
result.put("row_count", rowCount);
|
||||
result.put("command", command);
|
||||
return result;
|
||||
@Transactional
|
||||
public void updateDashboardCard(Map<String, Object> params) {
|
||||
sqlSession.update(NS + "updateDashboardCard", params);
|
||||
}
|
||||
|
||||
// ── table-schema ──────────────────────────────────────────────────────────────
|
||||
public Map<String, Object> getTableSchema(String tableName) {
|
||||
if (!tableName.matches("^[a-zA-Z_][a-zA-Z0-9_]*$")) {
|
||||
throw new IllegalArgumentException("유효하지 않은 테이블명입니다.");
|
||||
}
|
||||
List<Map<String, Object>> cols = jdbcTemplate.queryForList(
|
||||
"SELECT column_name, data_type, udt_name FROM information_schema.columns" +
|
||||
" WHERE table_name = ? ORDER BY ordinal_position",
|
||||
tableName.toLowerCase());
|
||||
|
||||
Set<String> dateTypes = new HashSet<>(Arrays.asList(
|
||||
"timestamp", "timestamp without time zone", "timestamp with time zone",
|
||||
"date", "time", "time without time zone", "time with time zone"));
|
||||
Set<String> dateUdt = new HashSet<>(Arrays.asList("timestamp", "timestamptz", "date", "time", "timetz"));
|
||||
|
||||
List<String> dateColumns = new ArrayList<>();
|
||||
List<Map<String, Object>> columns = new ArrayList<>();
|
||||
for (Map<String, Object> col : cols) {
|
||||
String dt = String.valueOf(col.get("data_type")).toLowerCase();
|
||||
String udt = String.valueOf(col.get("udt_name")).toLowerCase();
|
||||
if (dateTypes.contains(dt) || dateUdt.contains(udt)) {
|
||||
dateColumns.add(String.valueOf(col.get("column_name")));
|
||||
}
|
||||
Map<String, Object> c = new LinkedHashMap<>();
|
||||
c.put("name", col.get("column_name"));
|
||||
c.put("type", col.get("data_type"));
|
||||
c.put("udt_name", col.get("udt_name"));
|
||||
columns.add(c);
|
||||
}
|
||||
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
result.put("table_name", tableName);
|
||||
result.put("columns", columns);
|
||||
result.put("date_columns", dateColumns);
|
||||
return result;
|
||||
@Transactional
|
||||
public void deleteDashboardCard(Map<String, Object> params) {
|
||||
sqlSession.update(NS + "deleteDashboardCard", params);
|
||||
}
|
||||
|
||||
// ── helpers ───────────────────────────────────────────────────────────────────
|
||||
@Transactional
|
||||
@SuppressWarnings("unchecked")
|
||||
private void insertElements(String dashboardId, Object elementsObj) {
|
||||
if (!(elementsObj instanceof List)) return;
|
||||
List<?> elements = (List<?>) elementsObj;
|
||||
for (int i = 0; i < elements.size(); i++) {
|
||||
if (!(elements.get(i) instanceof Map)) continue;
|
||||
Map<String, Object> el = (Map<String, Object>) elements.get(i);
|
||||
String elementId = UUID.randomUUID().toString();
|
||||
Map<String, Object> position = el.get("position") instanceof Map ? (Map<String, Object>) el.get("position") : new HashMap<>();
|
||||
Map<String, Object> size = el.get("size") instanceof Map ? (Map<String, Object>) el.get("size") : new HashMap<>();
|
||||
jdbcTemplate.update(
|
||||
"INSERT INTO dashboard_elements (id, dashboard_id, element_type, element_subtype," +
|
||||
" position_x, position_y, width, height, title, custom_title, show_header, content," +
|
||||
" data_source_config, chart_config, list_config, yard_config, custom_metric_config," +
|
||||
" display_order, created_date, updated_date)" +
|
||||
" VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?::jsonb,?::jsonb,?::jsonb,?::jsonb,?::jsonb,?,NOW(),NOW())",
|
||||
elementId, dashboardId,
|
||||
el.get("type"), el.get("subtype"),
|
||||
toInt(position.get("x"), 0), toInt(position.get("y"), 0),
|
||||
toInt(size.get("width"), 4), toInt(size.get("height"), 3),
|
||||
el.get("title"), el.get("custom_title"),
|
||||
!Boolean.FALSE.equals(el.get("show_header")),
|
||||
el.get("content"),
|
||||
toJson(el.getOrDefault("data_source", new HashMap<>())),
|
||||
toJson(el.getOrDefault("chart_config", new HashMap<>())),
|
||||
el.get("list_config") != null ? toJson(el.get("list_config")) : null,
|
||||
el.get("yard_config") != null ? toJson(el.get("yard_config")) : null,
|
||||
el.get("custom_metric_config") != null ? toJson(el.get("custom_metric_config")) : null,
|
||||
i);
|
||||
public void updateCardPositions(Map<String, Object> params) {
|
||||
List<Map<String, Object>> cards = (List<Map<String, Object>>) params.get("cards");
|
||||
if (cards != null) {
|
||||
for (Map<String, Object> card : cards) {
|
||||
sqlSession.update(NS + "updateCardPosition", card);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Object> formatDashboardRow(Map<String, Object> row, boolean includeSettings) {
|
||||
Map<String, Object> d = new LinkedHashMap<>();
|
||||
d.put("id", row.get("id"));
|
||||
d.put("title", row.get("title"));
|
||||
d.put("description", row.get("description"));
|
||||
d.put("thumbnail_url", row.get("thumbnail_url"));
|
||||
d.put("is_public", row.get("is_public"));
|
||||
d.put("created_by", row.get("created_by"));
|
||||
d.put("created_by_name", row.get("created_by_name") != null ? row.get("created_by_name") : row.get("created_by"));
|
||||
d.put("created_date", row.get("created_date"));
|
||||
d.put("updated_date", row.get("updated_date"));
|
||||
d.put("tags", parseJson(row.get("tags"), new ArrayList<>()));
|
||||
d.put("category", row.get("category"));
|
||||
d.put("view_count", toInt(row.get("view_count"), 0));
|
||||
d.put("company_code", row.get("company_code"));
|
||||
if (row.containsKey("elements_count")) d.put("elements_count", toInt(row.get("elements_count"), 0));
|
||||
if (includeSettings) d.put("settings", parseJson(row.get("settings"), null));
|
||||
return d;
|
||||
}
|
||||
// ═══ 사이드바 메뉴 ═══
|
||||
|
||||
private Map<String, Object> formatElement(Map<String, Object> row) {
|
||||
Map<String, Object> el = new LinkedHashMap<>();
|
||||
el.put("id", row.get("id"));
|
||||
el.put("type", row.get("element_type"));
|
||||
el.put("subtype", row.get("element_subtype"));
|
||||
Map<String, Object> pos = new LinkedHashMap<>();
|
||||
pos.put("x", row.get("position_x"));
|
||||
pos.put("y", row.get("position_y"));
|
||||
el.put("position", pos);
|
||||
Map<String, Object> size = new LinkedHashMap<>();
|
||||
size.put("width", row.get("width"));
|
||||
size.put("height", row.get("height"));
|
||||
el.put("size", size);
|
||||
el.put("title", row.get("title"));
|
||||
el.put("custom_title", row.get("custom_title"));
|
||||
el.put("show_header", !Boolean.FALSE.equals(row.get("show_header")));
|
||||
el.put("content", row.get("content"));
|
||||
el.put("data_source", parseJson(row.get("data_source_config"), new HashMap<>()));
|
||||
el.put("chart_config", parseJson(row.get("chart_config"), new HashMap<>()));
|
||||
if (row.get("list_config") != null) el.put("list_config", parseJson(row.get("list_config"), null));
|
||||
if (row.get("yard_config") != null) el.put("yard_config", parseJson(row.get("yard_config"), null));
|
||||
if (row.get("custom_metric_config") != null) el.put("custom_metric_config", parseJson(row.get("custom_metric_config"), null));
|
||||
return el;
|
||||
}
|
||||
|
||||
private String toJson(Object obj) {
|
||||
if (obj == null) return "{}";
|
||||
if (obj instanceof String) return (String) obj;
|
||||
try { return objectMapper.writeValueAsString(obj); }
|
||||
catch (Exception e) { return "{}"; }
|
||||
}
|
||||
|
||||
private Object parseJson(Object value, Object defaultValue) {
|
||||
if (value == null) return defaultValue;
|
||||
String jsonStr = null;
|
||||
if (value instanceof String) {
|
||||
jsonStr = (String) value;
|
||||
} else if (value instanceof org.postgresql.util.PGobject pgObj) {
|
||||
jsonStr = pgObj.getValue();
|
||||
}
|
||||
if (jsonStr != null) {
|
||||
try { return objectMapper.readValue(jsonStr, new TypeReference<Object>() {}); }
|
||||
catch (Exception e) { return defaultValue; }
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private int toInt(Object v, int def) {
|
||||
if (v == null) return def;
|
||||
if (v instanceof Number) return ((Number) v).intValue();
|
||||
try { return Integer.parseInt(v.toString()); } catch (Exception e) { return def; }
|
||||
public List<Map<String, Object>> getSidebarMenu(Map<String, Object> params) {
|
||||
return sqlSession.selectList(NS + "getSidebarMenu", params);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,389 @@
|
||||
package com.erp.service;
|
||||
|
||||
import com.erp.common.BaseService;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
public class MetaService extends BaseService {
|
||||
|
||||
@Autowired
|
||||
private CommonService commonService;
|
||||
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
private static final String NS = "meta.";
|
||||
|
||||
private static final Set<String> SYSTEM_FIELDS = Set.of(
|
||||
"company_code", "created_by", "created_date", "updated_by", "updated_date",
|
||||
"is_active", "deleted_date", "deleted_by", "writer", "write_date"
|
||||
);
|
||||
|
||||
private static final Set<String> SEARCHABLE_TYPES = Set.of(
|
||||
"text", "select", "entity", "date", "code"
|
||||
);
|
||||
|
||||
// ──────────────────────────────────────────────────
|
||||
// 테이블 목록
|
||||
// ──────────────────────────────────────────────────
|
||||
|
||||
public List<Map<String, Object>> getMetaTableList(Map<String, Object> params) {
|
||||
return sqlSession.selectList(NS + "getMetaTableList", params);
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────────
|
||||
// FieldConfig[] 반환
|
||||
// ──────────────────────────────────────────────────
|
||||
|
||||
public Map<String, Object> getMetaFields(Map<String, Object> params) {
|
||||
String tableName = (String) params.get("table_name");
|
||||
|
||||
List<Map<String, Object>> schemaCols = sqlSession.selectList(NS + "getSchemaColumns", params);
|
||||
List<String> pks = sqlSession.selectList(NS + "getPrimaryKeys", params);
|
||||
List<Map<String, Object>> customMeta = sqlSession.selectList(NS + "getCustomMeta", params);
|
||||
|
||||
String tableLabel = sqlSession.selectOne(NS + "getTableLabel", params);
|
||||
if (tableLabel == null || tableLabel.isBlank()) {
|
||||
tableLabel = tableName;
|
||||
}
|
||||
|
||||
String primaryKey = pks.isEmpty() ? null : String.join(",", pks);
|
||||
List<Map<String, Object>> fields = buildFieldConfigs(schemaCols, pks, customMeta);
|
||||
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
result.put("table_name", tableName);
|
||||
result.put("table_label", tableLabel);
|
||||
result.put("primary_key", primaryKey);
|
||||
result.put("fields", fields);
|
||||
return result;
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────────
|
||||
// FieldConfig 빌드 (핵심)
|
||||
// ──────────────────────────────────────────────────
|
||||
|
||||
private List<Map<String, Object>> buildFieldConfigs(
|
||||
List<Map<String, Object>> schemaCols,
|
||||
List<String> pks,
|
||||
List<Map<String, Object>> customMeta
|
||||
) {
|
||||
Set<String> pkSet = new HashSet<>(pks);
|
||||
|
||||
Map<String, Map<String, Object>> metaMap = new LinkedHashMap<>();
|
||||
for (Map<String, Object> meta : customMeta) {
|
||||
String colName = str(meta, "column_name");
|
||||
if (colName != null) {
|
||||
metaMap.put(colName, meta);
|
||||
}
|
||||
}
|
||||
|
||||
List<Map<String, Object>> fields = new ArrayList<>();
|
||||
|
||||
for (Map<String, Object> schemaCol : schemaCols) {
|
||||
String columnName = str(schemaCol, "column_name");
|
||||
String dataType = str(schemaCol, "data_type");
|
||||
String isNullable = str(schemaCol, "is_nullable");
|
||||
String columnDefault = str(schemaCol, "column_default");
|
||||
int ordinalPosition = num(schemaCol, "ordinal_position");
|
||||
|
||||
Map<String, Object> meta = metaMap.get(columnName);
|
||||
Map<String, Object> detailSettings = meta != null ? parseDetailSettings(meta) : null;
|
||||
|
||||
boolean isPk = pkSet.contains(columnName);
|
||||
boolean isSystem = SYSTEM_FIELDS.contains(columnName);
|
||||
|
||||
// ── type ──
|
||||
String fieldType;
|
||||
if (meta != null && str(meta, "input_type") != null && !str(meta, "input_type").isBlank()) {
|
||||
fieldType = mapInputTypeToFieldType(str(meta, "input_type"));
|
||||
} else {
|
||||
fieldType = mapDataTypeToFieldType(dataType);
|
||||
}
|
||||
|
||||
// ── label ──
|
||||
String label = columnName;
|
||||
if (meta != null && str(meta, "column_label") != null && !str(meta, "column_label").isBlank()) {
|
||||
label = str(meta, "column_label");
|
||||
}
|
||||
|
||||
// ── order ──
|
||||
int order = ordinalPosition;
|
||||
if (meta != null && meta.get("display_order") != null) {
|
||||
int displayOrder = num(meta, "display_order");
|
||||
if (displayOrder > 0) order = displayOrder;
|
||||
}
|
||||
|
||||
// ── visible ──
|
||||
boolean visible = !isSystem;
|
||||
if (!isSystem && meta != null && meta.get("is_visible") != null) {
|
||||
String isVisible = meta.get("is_visible").toString();
|
||||
visible = "Y".equalsIgnoreCase(isVisible) || "true".equalsIgnoreCase(isVisible);
|
||||
}
|
||||
|
||||
// ── required (★ 앱 레벨 메타 우선, DB 스키마 폴백) ──
|
||||
boolean required;
|
||||
if (!isSystem && meta != null && meta.get("is_nullable") != null) {
|
||||
// table_type_columns.IS_NULLABLE가 있으면 앱 레벨 메타가 진실
|
||||
String metaNullable = meta.get("is_nullable").toString();
|
||||
required = "NO".equalsIgnoreCase(metaNullable);
|
||||
} else {
|
||||
// 없으면 information_schema 폴백
|
||||
required = "NO".equalsIgnoreCase(isNullable) && columnDefault == null && !isSystem;
|
||||
}
|
||||
|
||||
// ── editable ──
|
||||
boolean editable = !isPk && !isSystem;
|
||||
if ("code".equals(fieldType)) editable = false;
|
||||
|
||||
// ── searchable ──
|
||||
boolean searchable = !isSystem && SEARCHABLE_TYPES.contains(fieldType);
|
||||
|
||||
// ── build field map ──
|
||||
Map<String, Object> field = new LinkedHashMap<>();
|
||||
field.put("column", columnName);
|
||||
field.put("label", label);
|
||||
field.put("type", fieldType);
|
||||
field.put("visible", visible);
|
||||
field.put("order", order);
|
||||
field.put("width", getDefaultWidth(fieldType));
|
||||
field.put("align", "number".equals(fieldType) ? "right" : "left");
|
||||
field.put("required", required);
|
||||
field.put("editable", editable);
|
||||
field.put("pk", isPk);
|
||||
field.put("system", isSystem);
|
||||
field.put("searchable", searchable);
|
||||
field.put("sortable", !isSystem);
|
||||
field.put("format", getDefaultFormat(fieldType));
|
||||
field.put("options", null);
|
||||
field.put("ref", null);
|
||||
field.put("computed", null);
|
||||
|
||||
// ── entity → ref ──
|
||||
if ("entity".equals(fieldType) && meta != null) {
|
||||
Map<String, Object> ref = buildFieldRef(meta, detailSettings);
|
||||
if (ref != null) field.put("ref", ref);
|
||||
}
|
||||
|
||||
// ── select → options ──
|
||||
if ("select".equals(fieldType) && detailSettings != null) {
|
||||
List<Object> options = extractOptions(detailSettings);
|
||||
if (options != null && !options.isEmpty()) field.put("options", options);
|
||||
}
|
||||
|
||||
// ── computed ──
|
||||
if (detailSettings != null && detailSettings.containsKey("computed")) {
|
||||
String computed = detailSettings.get("computed").toString();
|
||||
if (!computed.isBlank()) {
|
||||
field.put("computed", computed);
|
||||
field.put("editable", false);
|
||||
}
|
||||
}
|
||||
|
||||
fields.add(field);
|
||||
}
|
||||
|
||||
fields.sort(Comparator.comparingInt(f -> num(f, "order")));
|
||||
return fields;
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────────
|
||||
// 타입 매핑
|
||||
// ──────────────────────────────────────────────────
|
||||
|
||||
private String mapDataTypeToFieldType(String dataType) {
|
||||
if (dataType == null) return "text";
|
||||
return switch (dataType.toLowerCase()) {
|
||||
case "character varying", "varchar" -> "text";
|
||||
case "text" -> "textarea";
|
||||
case "integer", "bigint", "smallint" -> "number";
|
||||
case "numeric", "decimal", "real", "double precision" -> "number";
|
||||
case "boolean" -> "checkbox";
|
||||
case "date" -> "date";
|
||||
case "timestamp without time zone", "timestamp with time zone" -> "datetime";
|
||||
case "jsonb", "json" -> "textarea";
|
||||
case "bytea" -> "file";
|
||||
default -> "text";
|
||||
};
|
||||
}
|
||||
|
||||
private String mapInputTypeToFieldType(String inputType) {
|
||||
if (inputType == null) return "text";
|
||||
return switch (inputType.toLowerCase()) {
|
||||
case "text" -> "text";
|
||||
case "number", "decimal" -> "number";
|
||||
case "date" -> "date";
|
||||
case "datetime" -> "datetime";
|
||||
case "select", "category" -> "select";
|
||||
case "entity" -> "entity";
|
||||
case "checkbox", "boolean" -> "checkbox";
|
||||
case "textarea", "text_area" -> "textarea";
|
||||
case "file" -> "file";
|
||||
case "code", "numbering" -> "code";
|
||||
case "email", "password", "tel" -> "text";
|
||||
default -> "text";
|
||||
};
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────────
|
||||
// entity ref 빌드
|
||||
// ──────────────────────────────────────────────────
|
||||
|
||||
private Map<String, Object> buildFieldRef(Map<String, Object> meta, Map<String, Object> detailSettings) {
|
||||
// 1차: top-level 컬럼
|
||||
String refTable = str(meta, "reference_table");
|
||||
String refColumn = str(meta, "reference_column");
|
||||
String displayCol = str(meta, "display_column");
|
||||
|
||||
// 2차: detail_settings JSON 폴백
|
||||
if ((refTable == null || refTable.isBlank()) && detailSettings != null) {
|
||||
refTable = strFromMap(detailSettings, "referenceTable");
|
||||
}
|
||||
if ((refColumn == null || refColumn.isBlank()) && detailSettings != null) {
|
||||
refColumn = strFromMap(detailSettings, "referenceColumn");
|
||||
}
|
||||
if ((displayCol == null || displayCol.isBlank()) && detailSettings != null) {
|
||||
displayCol = strFromMap(detailSettings, "displayColumn");
|
||||
}
|
||||
|
||||
if (refTable == null || refTable.isBlank()) return null;
|
||||
|
||||
Map<String, Object> ref = new LinkedHashMap<>();
|
||||
ref.put("table", refTable);
|
||||
ref.put("value_column", refColumn != null && !refColumn.isBlank() ? refColumn : "id");
|
||||
ref.put("display_column", displayCol != null && !displayCol.isBlank() ? displayCol : refColumn);
|
||||
|
||||
// search_columns
|
||||
if (detailSettings != null && detailSettings.containsKey("searchColumns")) {
|
||||
Object sc = detailSettings.get("searchColumns");
|
||||
if (sc instanceof List) {
|
||||
ref.put("search_columns", sc);
|
||||
}
|
||||
}
|
||||
|
||||
return ref;
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────────
|
||||
// select options 추출
|
||||
// ──────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* select options 추출.
|
||||
* ★ FieldOption 규격: string이면 value=label, 객체면 {value, label} 그대로 보존
|
||||
* - string[]: ["임시저장", "확정"] → 그대로 반환
|
||||
* - [{value,label}]: [{value:"DIRECT",label:"직접배송"}] → 그대로 반환
|
||||
* - 프론트에서 select 렌더러는 항상 value를 저장/전송, label을 표시
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<Object> extractOptions(Map<String, Object> detailSettings) {
|
||||
if (detailSettings == null || !detailSettings.containsKey("options")) return null;
|
||||
Object optionsObj = detailSettings.get("options");
|
||||
if (!(optionsObj instanceof List)) return null;
|
||||
|
||||
List<?> optionsList = (List<?>) optionsObj;
|
||||
if (optionsList.isEmpty()) return null;
|
||||
|
||||
List<Object> result = new ArrayList<>();
|
||||
for (Object item : optionsList) {
|
||||
if (item instanceof String) {
|
||||
// 단순 문자열: value=label로 해석
|
||||
result.add(item);
|
||||
} else if (item instanceof Map) {
|
||||
// {value, label} 객체: 그대로 보존
|
||||
Map<String, Object> optMap = (Map<String, Object>) item;
|
||||
Map<String, Object> opt = new LinkedHashMap<>();
|
||||
Object value = optMap.get("value");
|
||||
Object label = optMap.get("label");
|
||||
opt.put("value", value != null ? value.toString() : "");
|
||||
opt.put("label", label != null ? label.toString() : (value != null ? value.toString() : ""));
|
||||
result.add(opt);
|
||||
}
|
||||
}
|
||||
return result.isEmpty() ? null : result;
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────────
|
||||
// detail_settings 파싱
|
||||
// ──────────────────────────────────────────────────
|
||||
|
||||
private Map<String, Object> parseDetailSettings(Map<String, Object> meta) {
|
||||
Object detailObj = meta.get("detail_settings");
|
||||
if (detailObj == null) return null;
|
||||
|
||||
String detailStr = detailObj.toString().trim();
|
||||
if (detailStr.isEmpty() || "{}".equals(detailStr)) return null;
|
||||
|
||||
try {
|
||||
return objectMapper.readValue(detailStr, new TypeReference<Map<String, Object>>() {});
|
||||
} catch (Exception e) {
|
||||
log.warn("detail_settings 파싱 실패: {}", detailStr);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────────
|
||||
// 기본값
|
||||
// ──────────────────────────────────────────────────
|
||||
|
||||
private int getDefaultWidth(String fieldType) {
|
||||
return switch (fieldType) {
|
||||
case "number" -> 100;
|
||||
case "date" -> 120;
|
||||
case "datetime" -> 160;
|
||||
case "select" -> 130;
|
||||
case "entity" -> 180;
|
||||
case "checkbox" -> 80;
|
||||
case "code" -> 120;
|
||||
case "textarea" -> 200;
|
||||
default -> 150;
|
||||
};
|
||||
}
|
||||
|
||||
private String getDefaultFormat(String fieldType) {
|
||||
return switch (fieldType) {
|
||||
case "number" -> "#,##0";
|
||||
case "date" -> "YYYY-MM-DD";
|
||||
case "datetime" -> "YYYY-MM-DD HH:mm";
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────────
|
||||
// 유틸
|
||||
// ──────────────────────────────────────────────────
|
||||
|
||||
private String str(Map<String, Object> map, String key) {
|
||||
Object val = map.get(key);
|
||||
return val != null ? val.toString() : null;
|
||||
}
|
||||
|
||||
private String strFromMap(Map<String, Object> map, String key) {
|
||||
Object val = map.get(key);
|
||||
return val != null ? val.toString() : null;
|
||||
}
|
||||
|
||||
private int num(Map<String, Object> map, String key) {
|
||||
Object val = map.get(key);
|
||||
if (val instanceof Number) return ((Number) val).intValue();
|
||||
if (val != null) {
|
||||
try { return Integer.parseInt(val.toString()); }
|
||||
catch (NumberFormatException e) { return 0; }
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────────
|
||||
// 테이블 간 업무 관계 (Phase 5 — 제어 모드)
|
||||
// ──────────────────────────────────────────────────
|
||||
|
||||
public List<Map<String, Object>> getMetaRelations(Map<String, Object> params) {
|
||||
return sqlSession.selectList(NS + "getMetaRelations", params);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
package com.erp.service;
|
||||
|
||||
import com.erp.common.BaseService;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
public class TemplateService extends BaseService {
|
||||
|
||||
@Autowired
|
||||
private CommonService commonService;
|
||||
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
private static final String NS = "template.";
|
||||
|
||||
// ──────────────────────────────────────────────────
|
||||
// 목록
|
||||
// ──────────────────────────────────────────────────
|
||||
|
||||
public Map<String, Object> getTemplateList(Map<String, Object> params) {
|
||||
commonService.applyPagination(params);
|
||||
int totalCount = sqlSession.selectOne(NS + "getTemplateListCnt", params);
|
||||
List<Map<String, Object>> list = sqlSession.selectList(NS + "getTemplateList", params);
|
||||
return commonService.buildListResponse(list, totalCount, params);
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────────
|
||||
// 단건
|
||||
// ──────────────────────────────────────────────────
|
||||
|
||||
public Map<String, Object> getTemplateInfo(Map<String, Object> params) {
|
||||
Map<String, Object> row = sqlSession.selectOne(NS + "getTemplateInfo", params);
|
||||
if (row == null) return null;
|
||||
|
||||
// JSONB 문자열 → 객체 변환
|
||||
parseJsonField(row, "fields");
|
||||
parseJsonField(row, "views");
|
||||
parseJsonField(row, "connections");
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────────
|
||||
// 등록
|
||||
// ──────────────────────────────────────────────────
|
||||
|
||||
@Transactional
|
||||
public Map<String, Object> insertTemplate(Map<String, Object> params) {
|
||||
String templateId = "tpl_" + UUID.randomUUID().toString().replace("-", "").substring(0, 12);
|
||||
params.put("template_id", templateId);
|
||||
|
||||
// 객체 → JSON 문자열 변환
|
||||
stringifyJsonField(params, "fields");
|
||||
stringifyJsonField(params, "views");
|
||||
stringifyJsonField(params, "connections");
|
||||
|
||||
sqlSession.insert(NS + "insertTemplate", params);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("template_id", templateId);
|
||||
return result;
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────────
|
||||
// 수정
|
||||
// ──────────────────────────────────────────────────
|
||||
|
||||
@Transactional
|
||||
public void updateTemplate(Map<String, Object> params) {
|
||||
stringifyJsonField(params, "fields");
|
||||
stringifyJsonField(params, "views");
|
||||
stringifyJsonField(params, "connections");
|
||||
|
||||
sqlSession.update(NS + "updateTemplate", params);
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────────
|
||||
// 게시
|
||||
// ──────────────────────────────────────────────────
|
||||
|
||||
@Transactional
|
||||
public void publishTemplate(Map<String, Object> params) {
|
||||
sqlSession.update(NS + "publishTemplate", params);
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────────
|
||||
// 삭제 (소프트)
|
||||
// ──────────────────────────────────────────────────
|
||||
|
||||
@Transactional
|
||||
public int deleteTemplate(Map<String, Object> params) {
|
||||
return sqlSession.update(NS + "deleteTemplate", params);
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────────
|
||||
// JSON 유틸
|
||||
// ──────────────────────────────────────────────────
|
||||
|
||||
private void parseJsonField(Map<String, Object> row, String key) {
|
||||
Object val = row.get(key);
|
||||
if (val instanceof String) {
|
||||
try {
|
||||
row.put(key, objectMapper.readValue((String) val, Object.class));
|
||||
} catch (Exception e) {
|
||||
log.warn("JSON 파싱 실패: key={}, value={}", key, val, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void stringifyJsonField(Map<String, Object> params, String key) {
|
||||
Object val = params.get(key);
|
||||
if (val != null && !(val instanceof String)) {
|
||||
try {
|
||||
params.put(key, objectMapper.writeValueAsString(val));
|
||||
} catch (Exception e) {
|
||||
log.warn("JSON 직렬화 실패: key={}", key, e);
|
||||
params.put(key, "[]");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.erp.service;
|
||||
|
||||
import com.erp.common.BaseService;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
public class UserOverrideService extends BaseService {
|
||||
|
||||
@Autowired
|
||||
private CommonService commonService;
|
||||
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
private static final String NS = "userOverride.";
|
||||
|
||||
public Map<String, Object> getUserOverride(Map<String, Object> params) {
|
||||
Map<String, Object> row = sqlSession.selectOne(NS + "getUserOverride", params);
|
||||
if (row != null) {
|
||||
parseJsonField(row, "overrides");
|
||||
}
|
||||
return row;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void upsertUserOverride(Map<String, Object> params) {
|
||||
if (params.get("override_id") == null) {
|
||||
String overrideId = "ovr_" + UUID.randomUUID().toString().replace("-", "").substring(0, 12);
|
||||
params.put("override_id", overrideId);
|
||||
}
|
||||
stringifyJsonField(params, "overrides");
|
||||
sqlSession.insert(NS + "upsertUserOverride", params);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void deleteUserOverride(Map<String, Object> params) {
|
||||
sqlSession.delete(NS + "deleteUserOverride", params);
|
||||
}
|
||||
|
||||
private void parseJsonField(Map<String, Object> row, String key) {
|
||||
Object val = row.get(key);
|
||||
if (val instanceof String) {
|
||||
try {
|
||||
row.put(key, objectMapper.readValue((String) val, Object.class));
|
||||
} catch (Exception e) {
|
||||
log.warn("JSON parse failed: key={}", key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void stringifyJsonField(Map<String, Object> params, String key) {
|
||||
Object val = params.get(key);
|
||||
if (val != null && !(val instanceof String)) {
|
||||
try {
|
||||
params.put(key, objectMapper.writeValueAsString(val));
|
||||
} catch (Exception e) {
|
||||
log.warn("JSON stringify failed: key={}", key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user