사용자 대시보드 기능강화 및 인비온 스튜디오 메뉴관리 자잘한수정
Build & Deploy to K8s / build-and-deploy (push) Successful in 4m22s

This commit is contained in:
DDD1542
2026-04-22 18:27:06 +09:00
parent a5de92de65
commit 3eda684787
33 changed files with 3964 additions and 560 deletions
@@ -550,15 +550,147 @@ public class EntityJoinService extends BaseService {
if (searchConditions == null || searchConditions.isEmpty()) return "";
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, Object> e : searchConditions.entrySet()) {
if (e.getValue() == null) continue;
if (isKeywordSearchKey(e.getKey())) {
SearchCondition condition = normalizeSearchCondition(e.getValue());
if (condition == null || isBlankValue(condition.value())) continue;
appendKeywordSearchCondition(sb, params, tableName, condition);
continue;
}
if (!IDENTIFIER_PATTERN.matcher(e.getKey()).matches()) continue;
SearchCondition condition = normalizeSearchCondition(e.getValue());
if (condition == null) continue;
if (sb.length() > 0) sb.append(" AND ");
sb.append("main.\"").append(e.getKey()).append("\" = ?");
params.add(e.getValue());
appendSearchCondition(sb, params, e.getKey(), condition);
}
return sb.toString();
}
@SuppressWarnings("unchecked")
private SearchCondition normalizeSearchCondition(Object rawValue) {
if (rawValue == null) return null;
if (rawValue instanceof Map<?, ?> rawMap) {
Map<String, Object> map = (Map<String, Object>) rawMap;
String operator = String.valueOf(map.getOrDefault("operator", "contains"));
Object value = map.get("value");
if (!"isEmpty".equalsIgnoreCase(operator)
&& !"isNotEmpty".equalsIgnoreCase(operator)
&& isBlankValue(value)) {
return null;
}
return new SearchCondition(operator, value);
}
if (isBlankValue(rawValue)) return null;
return new SearchCondition("equals", rawValue);
}
private void appendSearchCondition(StringBuilder sb,
List<Object> params,
String columnName,
SearchCondition condition) {
String op = condition.operator();
Object value = condition.value();
String colExpr = "main.\"" + columnName + "\"";
switch (op.toLowerCase()) {
case "contains" -> {
sb.append(colExpr).append("::text ILIKE ?");
params.add("%" + value + "%");
}
case "startswith" -> {
sb.append(colExpr).append("::text ILIKE ?");
params.add(value + "%");
}
case "endswith" -> {
sb.append(colExpr).append("::text ILIKE ?");
params.add("%" + value);
}
case "notequals" -> {
sb.append("COALESCE(").append(colExpr).append("::text, '') <> ?");
params.add(String.valueOf(value));
}
case "isempty" -> sb.append("(").append(colExpr).append(" IS NULL OR ")
.append(colExpr).append("::text = '')");
case "isnotempty" -> sb.append("(").append(colExpr).append(" IS NOT NULL AND ")
.append(colExpr).append("::text <> '')");
case "greaterthan" -> {
sb.append(colExpr).append("::text > ?");
params.add(String.valueOf(value));
}
case "lessthan" -> {
sb.append(colExpr).append("::text < ?");
params.add(String.valueOf(value));
}
case "equals", "=", "exact" -> {
sb.append(colExpr).append("::text = ?");
params.add(String.valueOf(value));
}
default -> {
sb.append(colExpr).append("::text ILIKE ?");
params.add("%" + value + "%");
}
}
}
private void appendKeywordSearchCondition(StringBuilder sb,
List<Object> params,
String tableName,
SearchCondition condition) {
List<String> columns = getKeywordSearchColumns(tableName);
if (columns.isEmpty()) return;
if (sb.length() > 0) sb.append(" AND ");
sb.append("(");
for (int i = 0; i < columns.size(); i++) {
if (i > 0) sb.append(" OR ");
sb.append("main.\"").append(columns.get(i)).append("\"::text ILIKE ?");
params.add("%" + String.valueOf(condition.value()) + "%");
}
sb.append(")");
}
private List<String> getKeywordSearchColumns(String tableName) {
validateIdentifier(tableName);
String sql = """
SELECT column_name
FROM information_schema.columns
WHERE table_schema = current_schema()
AND table_name = ?
ORDER BY ordinal_position
""";
List<String> allColumns = jdbcTemplate.queryForList(sql, String.class, tableName);
return allColumns.stream()
.filter(Objects::nonNull)
.filter(col -> IDENTIFIER_PATTERN.matcher(col).matches())
.filter(col -> !Set.of(
"company_code",
"created_date",
"updated_date",
"created_at",
"updated_at",
"create_user",
"update_user"
).contains(col))
.toList();
}
private boolean isKeywordSearchKey(String key) {
return "keyword".equalsIgnoreCase(key)
|| "search".equalsIgnoreCase(key)
|| "searchterm".equalsIgnoreCase(key)
|| "q".equalsIgnoreCase(key);
}
private boolean isBlankValue(Object value) {
return value == null || value.toString().isBlank() || "__ALL__".equals(value.toString());
}
private void validateIdentifier(String name) {
if (name == null || !IDENTIFIER_PATTERN.matcher(name).matches()) {
throw new IllegalArgumentException("유효하지 않은 식별자: " + name);
@@ -586,4 +718,6 @@ public class EntityJoinService extends BaseService {
return total == 0 ? 0.0 : (double) hits / total;
}
}
private record SearchCondition(String operator, Object value) {}
}