Files
vpd-permission-poc/src/main/resources/templates/schema-metadata.html

199 lines
10 KiB
HTML

<!doctype html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head th:replace="~{fragments/layout :: head('DB 메타데이터')}"></head>
<body>
<nav th:replace="~{fragments/layout :: nav}"></nav>
<main class="container py-4">
<section class="page-title">
<span class="badge text-bg-primary">Select AI 근거 메타데이터</span>
<h1 class="mt-2">DB 메타데이터</h1>
<details class="explanation-details">
<summary>도움말</summary>
<p>
<code>SGMP_POC</code> 게임 데이터 테이블의 table/column comment와 Oracle annotation을 조회·수정합니다.
Select AI profile의 <code>comments=true</code>, <code>annotations=true</code> 설정에서는 이 값들이 SQL 생성 근거로 들어갑니다.
</p>
<p class="mb-0">임의 스키마나 임의 테이블은 수정하지 않고, 백오피스가 승인한 7개 업무 테이블만 대상으로 합니다.</p>
</details>
</section>
<section class="alert alert-success" th:if="${message}" th:text="${message}">저장 완료</section>
<section class="alert alert-danger" th:if="${errorMessage}" th:text="${errorMessage}">오류</section>
<section class="content-band">
<div class="section-heading">
<div>
<h2>테이블 선택</h2>
<p class="section-subtitle">정형 MCP/Select AI가 참조하는 게임 데이터 테이블만 표시합니다.</p>
</div>
<span class="badge text-bg-secondary" th:text="${#lists.size(tables)}">7</span>
</div>
<div class="structured-table-grid">
<a th:each="entry : ${tables}"
class="structured-table-card"
th:classappend="${entry.key() == selectedKey} ? ' is-selected'"
th:href="@{/schema-metadata(table=${entry.key()})}">
<strong th:text="${entry.businessName()}">게임 사용자</strong>
<code th:text="${entry.tableName()}">CZN_COMN_USER_MST</code>
<small th:text="${entry.description()}">게임 사용자 마스터</small>
</a>
</div>
</section>
<section class="content-band" th:if="${metadata}">
<div class="section-heading">
<div>
<span class="badge text-bg-secondary">TABLE</span>
<h2 class="mt-2" th:text="${metadata.table().businessName()}">게임 데이터</h2>
<p class="section-subtitle">
<code th:text="${'SGMP_POC.' + metadata.table().tableName()}">SGMP_POC.CZN_COMN_USER_MST</code>
<span th:text="${' · ' + metadata.table().description()}"> · 설명</span>
</p>
</div>
<a class="btn btn-sm rw-btn-secondary" th:href="@{/structured-data(table=${metadata.table().key()})}">데이터 미리보기</a>
</div>
<form method="post" action="/schema-metadata/table-comment" class="mb-4">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
<input type="hidden" name="table" th:value="${metadata.table().key()}">
<label class="form-label fw-semibold">Table comment</label>
<textarea class="form-control" rows="3" name="comment" maxlength="4000"
th:text="${metadata.tableComment()}"></textarea>
<div class="form-hint mt-1">Oracle <code>COMMENT ON TABLE</code>로 즉시 반영됩니다. 빈 값으로 저장하면 comment를 비웁니다.</div>
<button class="btn rw-btn-primary mt-2" type="submit">테이블 comment 저장</button>
</form>
<div class="section-heading">
<div>
<h3>Table annotations</h3>
<p class="section-subtitle">기존 annotation은 값 수정 또는 빈 값 저장으로 삭제할 수 있습니다.</p>
</div>
</div>
<div class="table-responsive">
<table class="table table-sm align-middle">
<thead><tr><th style="width: 220px;">Annotation</th><th>Value</th><th style="width: 110px;"></th></tr></thead>
<tbody>
<tr th:each="annotation : ${metadata.tableAnnotations()}">
<td><code th:text="${annotation.name()}">DISPLAY_NAME</code></td>
<td>
<form method="post" action="/schema-metadata/table-annotation" class="metadata-inline-form">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
<input type="hidden" name="table" th:value="${metadata.table().key()}">
<input type="hidden" name="annotationName" th:value="${annotation.name()}">
<textarea class="form-control" rows="2" name="annotationValue" maxlength="4000"
th:text="${annotation.value()}"></textarea>
<div class="form-hint mt-1">저장 전 기존 같은 key는 DROP 후 다시 ADD합니다.</div>
<button class="btn btn-sm rw-btn-primary mt-2" type="submit">저장</button>
</form>
</td>
<td class="text-muted">기존</td>
</tr>
<tr th:if="${#lists.isEmpty(metadata.tableAnnotations())}">
<td colspan="3" class="text-muted">등록된 table annotation이 없습니다.</td>
</tr>
<tr>
<td colspan="3">
<form method="post" action="/schema-metadata/table-annotation" class="row g-2 align-items-start">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
<input type="hidden" name="table" th:value="${metadata.table().key()}">
<div class="col-md-3">
<input class="form-control" name="annotationName" placeholder="예: QUERY_HINT" maxlength="128">
</div>
<div class="col-md-7">
<textarea class="form-control" rows="2" name="annotationValue" maxlength="4000"
placeholder="Select AI가 참고할 업무 의미, 조인 힌트, 값 도메인 등을 입력"></textarea>
</div>
<div class="col-md-2">
<button class="btn rw-btn-secondary w-100" type="submit">추가</button>
</div>
</form>
</td>
</tr>
</tbody>
</table>
</div>
</section>
<section class="content-band" th:if="${metadata}">
<div class="section-heading">
<div>
<h2>Column comments / annotations</h2>
<p class="section-subtitle">컬럼 comment와 컬럼 annotation을 수정합니다. 저장 즉시 DB dictionary에 반영됩니다.</p>
</div>
<span class="badge text-bg-secondary" th:text="${#lists.size(metadata.columns()) + '개 컬럼'}">0개 컬럼</span>
</div>
<div class="metadata-column-stack">
<details class="content-band nested-content-band"
th:each="column : ${metadata.columns()}"
th:open="${!#lists.isEmpty(column.annotations())}">
<summary class="d-flex justify-content-between align-items-center gap-3">
<span>
<code th:text="${column.columnName()}">CONTRACT_NO</code>
<small class="text-muted ms-2" th:text="${column.dataType()}">VARCHAR2(30)</small>
<span class="badge text-bg-light ms-2" th:text="${column.nullable()} ? 'NULL 허용' : 'NOT NULL'">NOT NULL</span>
</span>
<span class="text-muted" th:text="${#lists.size(column.annotations()) + ' annotations'}">0 annotations</span>
</summary>
<form method="post" action="/schema-metadata/column-comment" class="mt-3">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
<input type="hidden" name="table" th:value="${metadata.table().key()}">
<input type="hidden" name="column" th:value="${column.columnName()}">
<label class="form-label fw-semibold">Column comment</label>
<textarea class="form-control" rows="2" name="comment" maxlength="4000"
th:text="${column.comment()}"></textarea>
<button class="btn btn-sm rw-btn-primary mt-2" type="submit">컬럼 comment 저장</button>
</form>
<div class="table-responsive mt-3">
<table class="table table-sm align-middle">
<thead><tr><th style="width: 220px;">Annotation</th><th>Value</th><th style="width: 110px;"></th></tr></thead>
<tbody>
<tr th:each="annotation : ${column.annotations()}">
<td><code th:text="${annotation.name()}">DISPLAY_NAME</code></td>
<td>
<form method="post" action="/schema-metadata/column-annotation">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
<input type="hidden" name="table" th:value="${metadata.table().key()}">
<input type="hidden" name="column" th:value="${column.columnName()}">
<input type="hidden" name="annotationName" th:value="${annotation.name()}">
<textarea class="form-control" rows="2" name="annotationValue" maxlength="4000"
th:text="${annotation.value()}"></textarea>
<button class="btn btn-sm rw-btn-primary mt-2" type="submit">저장</button>
</form>
</td>
<td class="text-muted">기존</td>
</tr>
<tr th:if="${#lists.isEmpty(column.annotations())}">
<td colspan="3" class="text-muted">등록된 column annotation이 없습니다.</td>
</tr>
<tr>
<td colspan="3">
<form method="post" action="/schema-metadata/column-annotation" class="row g-2 align-items-start">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
<input type="hidden" name="table" th:value="${metadata.table().key()}">
<input type="hidden" name="column" th:value="${column.columnName()}">
<div class="col-md-3">
<input class="form-control" name="annotationName" placeholder="예: VALUE_DOMAIN" maxlength="128">
</div>
<div class="col-md-7">
<textarea class="form-control" rows="2" name="annotationValue" maxlength="4000"
placeholder="컬럼 표시명, 값 범위, 조인 대상, 민감도 등을 입력"></textarea>
</div>
<div class="col-md-2">
<button class="btn rw-btn-secondary w-100" type="submit">추가</button>
</div>
</form>
</td>
</tr>
</tbody>
</table>
</div>
</details>
</div>
</section>
</main>
</body>
</html>