[Developer] #424 add backoffice schema initialization action
This commit is contained in:
@@ -0,0 +1,207 @@
|
||||
package com.cloudhandson.vpdbackoffice.service;
|
||||
|
||||
import com.cloudhandson.vpdbackoffice.config.BackofficeProperties;
|
||||
import com.cloudhandson.vpdbackoffice.domain.schema.SchemaActionResult;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
public class BackofficeSchemaService {
|
||||
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
private final BackofficeProperties properties;
|
||||
|
||||
public BackofficeSchemaService(JdbcTemplate jdbcTemplate, BackofficeProperties properties) {
|
||||
this.jdbcTemplate = jdbcTemplate;
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public List<SchemaActionResult> initializeSchema() {
|
||||
List<SchemaActionResult> results = new ArrayList<>();
|
||||
createSequence(results, "cb_agent_bearer_key_seq", 1000);
|
||||
createSequence(results, "cb_permission_seq", 1000);
|
||||
createSequence(results, "cb_permission_rule_seq", 10000);
|
||||
createSequence(results, "cb_ords_probe_audit_seq", 1);
|
||||
|
||||
createTable(results, "cb_app_user", """
|
||||
CREATE TABLE cb_app_user (
|
||||
user_id NUMBER PRIMARY KEY,
|
||||
user_name VARCHAR2(100) NOT NULL UNIQUE,
|
||||
employee_no VARCHAR2(50),
|
||||
dept_code VARCHAR2(50),
|
||||
can_read_contents CHAR(1) DEFAULT 'N' CHECK (can_read_contents IN ('Y','N')) NOT NULL,
|
||||
active CHAR(1) DEFAULT 'Y' CHECK (active IN ('Y','N')) NOT NULL
|
||||
)
|
||||
""");
|
||||
createTable(results, "cb_app_role", """
|
||||
CREATE TABLE cb_app_role (
|
||||
role_id NUMBER PRIMARY KEY,
|
||||
role_name VARCHAR2(100) NOT NULL UNIQUE
|
||||
)
|
||||
""");
|
||||
createTable(results, "cb_user_role", """
|
||||
CREATE TABLE cb_user_role (
|
||||
user_id NUMBER NOT NULL,
|
||||
role_id NUMBER NOT NULL,
|
||||
CONSTRAINT cb_user_role_pk PRIMARY KEY (user_id, role_id)
|
||||
)
|
||||
""");
|
||||
createTable(results, "cb_permission", """
|
||||
CREATE TABLE cb_permission (
|
||||
perm_id NUMBER PRIMARY KEY,
|
||||
role_id NUMBER NOT NULL,
|
||||
target_name VARCHAR2(128) NOT NULL,
|
||||
action_name VARCHAR2(30) DEFAULT 'SELECT' NOT NULL
|
||||
)
|
||||
""");
|
||||
createTable(results, "cb_permission_rule", """
|
||||
CREATE TABLE cb_permission_rule (
|
||||
rule_id NUMBER PRIMARY KEY,
|
||||
perm_id NUMBER NOT NULL,
|
||||
rule_column VARCHAR2(128),
|
||||
rule_type VARCHAR2(30) NOT NULL,
|
||||
rule_value VARCHAR2(4000)
|
||||
)
|
||||
""");
|
||||
addColumn(results, "cb_permission_rule", "rule_column", "ALTER TABLE cb_permission_rule ADD (rule_column VARCHAR2(128))");
|
||||
createTable(results, "cb_permission_column", """
|
||||
CREATE TABLE cb_permission_column (
|
||||
permission_id NUMBER NOT NULL,
|
||||
column_name VARCHAR2(128) NOT NULL,
|
||||
CONSTRAINT cb_permission_column_pk PRIMARY KEY (permission_id, column_name)
|
||||
)
|
||||
""");
|
||||
createTable(results, "cb_agent_bearer_key", """
|
||||
CREATE TABLE cb_agent_bearer_key (
|
||||
key_id NUMBER PRIMARY KEY,
|
||||
user_id NUMBER NOT NULL,
|
||||
key_prefix VARCHAR2(30) NOT NULL,
|
||||
key_hash VARCHAR2(128) NOT NULL UNIQUE,
|
||||
expires_at TIMESTAMP NOT NULL,
|
||||
revoked_at TIMESTAMP,
|
||||
description VARCHAR2(200)
|
||||
)
|
||||
""");
|
||||
addColumn(results, "cb_agent_bearer_key", "description",
|
||||
"ALTER TABLE cb_agent_bearer_key ADD (description VARCHAR2(200))");
|
||||
createTable(results, "cb_protected_object", """
|
||||
CREATE TABLE cb_protected_object (
|
||||
object_id NUMBER PRIMARY KEY,
|
||||
owner VARCHAR2(128) NOT NULL,
|
||||
object_name VARCHAR2(128) NOT NULL UNIQUE,
|
||||
ords_path VARCHAR2(300) NOT NULL,
|
||||
enabled_yn CHAR(1) DEFAULT 'Y' CHECK (enabled_yn IN ('Y','N')) NOT NULL
|
||||
)
|
||||
""");
|
||||
createTable(results, "cb_protected_column", """
|
||||
CREATE TABLE cb_protected_column (
|
||||
column_id NUMBER PRIMARY KEY,
|
||||
object_id NUMBER NOT NULL,
|
||||
column_name VARCHAR2(128) NOT NULL,
|
||||
sensitive_yn CHAR(1) DEFAULT 'N' CHECK (sensitive_yn IN ('Y','N')) NOT NULL,
|
||||
visible_role_id NUMBER,
|
||||
CONSTRAINT cb_protected_column_uk UNIQUE (object_id, column_name)
|
||||
)
|
||||
""");
|
||||
createTable(results, "cb_ords_probe_audit", """
|
||||
CREATE TABLE cb_ords_probe_audit (
|
||||
audit_id NUMBER PRIMARY KEY,
|
||||
event_type VARCHAR2(50) NOT NULL,
|
||||
key_id NUMBER,
|
||||
object_id NUMBER,
|
||||
status VARCHAR2(50),
|
||||
row_count NUMBER,
|
||||
error_code VARCHAR2(100),
|
||||
message VARCHAR2(500),
|
||||
created_at TIMESTAMP DEFAULT SYSTIMESTAMP NOT NULL
|
||||
)
|
||||
""");
|
||||
createTable(results, "cb_backoffice_setting", """
|
||||
CREATE TABLE cb_backoffice_setting (
|
||||
setting_key VARCHAR2(100) PRIMARY KEY,
|
||||
setting_value VARCHAR2(1000),
|
||||
updated_at TIMESTAMP DEFAULT SYSTIMESTAMP NOT NULL
|
||||
)
|
||||
""");
|
||||
seedDefaultSettings(results);
|
||||
return results;
|
||||
}
|
||||
|
||||
private void createSequence(List<SchemaActionResult> results, String name, int startWith) {
|
||||
runIgnoringAlreadyExists(results, name, "SEQUENCE",
|
||||
"CREATE SEQUENCE " + name + " START WITH " + startWith + " INCREMENT BY 1 NOCACHE");
|
||||
}
|
||||
|
||||
private void createTable(List<SchemaActionResult> results, String name, String ddl) {
|
||||
runIgnoringAlreadyExists(results, name, "TABLE", ddl);
|
||||
}
|
||||
|
||||
private void addColumn(List<SchemaActionResult> results, String table, String column, String ddl) {
|
||||
try {
|
||||
jdbcTemplate.execute(ddl);
|
||||
results.add(new SchemaActionResult(table + "." + column, "COLUMN", "CREATED", "컬럼을 추가했습니다."));
|
||||
} catch (RuntimeException exception) {
|
||||
if (oracleErrorCode(exception) == 1430) {
|
||||
results.add(new SchemaActionResult(table + "." + column, "COLUMN", "EXISTS", "이미 존재합니다."));
|
||||
return;
|
||||
}
|
||||
results.add(new SchemaActionResult(table + "." + column, "COLUMN", "FAILED", safeMessage(exception)));
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
||||
private void runIgnoringAlreadyExists(List<SchemaActionResult> results, String name, String action, String ddl) {
|
||||
try {
|
||||
jdbcTemplate.execute(ddl);
|
||||
results.add(new SchemaActionResult(name, action, "CREATED", "생성했습니다."));
|
||||
} catch (RuntimeException exception) {
|
||||
if (oracleErrorCode(exception) == 955) {
|
||||
results.add(new SchemaActionResult(name, action, "EXISTS", "이미 존재합니다."));
|
||||
return;
|
||||
}
|
||||
results.add(new SchemaActionResult(name, action, "FAILED", safeMessage(exception)));
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
||||
private void seedDefaultSettings(List<SchemaActionResult> results) {
|
||||
jdbcTemplate.update("""
|
||||
MERGE INTO cb_backoffice_setting dst
|
||||
USING (
|
||||
SELECT 'ORDS_BASE_URL' setting_key, ? setting_value
|
||||
FROM dual
|
||||
) src
|
||||
ON (dst.setting_key = src.setting_key)
|
||||
WHEN MATCHED THEN UPDATE SET dst.setting_value = src.setting_value, dst.updated_at = SYSTIMESTAMP
|
||||
WHERE dst.setting_value IS NULL OR TRIM(dst.setting_value) IS NULL
|
||||
WHEN NOT MATCHED THEN INSERT (setting_key, setting_value, updated_at)
|
||||
VALUES (src.setting_key, src.setting_value, SYSTIMESTAMP)
|
||||
""", properties.ords().baseUrl());
|
||||
results.add(new SchemaActionResult("ORDS_BASE_URL", "SETTING", "MERGED", "기본 ORDS URL 설정을 보강했습니다."));
|
||||
}
|
||||
|
||||
private int oracleErrorCode(RuntimeException exception) {
|
||||
Throwable current = exception;
|
||||
while (current != null) {
|
||||
if (current instanceof SQLException sqlException) {
|
||||
return Math.abs(sqlException.getErrorCode());
|
||||
}
|
||||
current = current.getCause();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private String safeMessage(Throwable throwable) {
|
||||
String message = throwable.getMessage();
|
||||
if (message == null || message.isBlank()) {
|
||||
return throwable.getClass().getSimpleName();
|
||||
}
|
||||
String normalized = message.replaceAll("\\s+", " ").trim();
|
||||
return normalized.length() <= 300 ? normalized : normalized.substring(0, 300) + "...";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user