diff --git a/backend-spring/src/main/java/com/erp/controller/AdminController.java b/backend-spring/src/main/java/com/erp/controller/AdminController.java index 978ad236..a1c8d745 100644 --- a/backend-spring/src/main/java/com/erp/controller/AdminController.java +++ b/backend-spring/src/main/java/com/erp/controller/AdminController.java @@ -295,7 +295,8 @@ public class AdminController { @PostMapping("/users/reset-password") public ResponseEntity> resetUserPassword(@RequestBody Map body) { String userId = (String) body.get("user_id"); - adminService.resetUserPassword(userId); + String newPassword = (String) body.get("new_password"); + adminService.resetUserPassword(userId, newPassword); return ResponseEntity.ok(ApiResponse.success(null, "비밀번호 초기화 성공")); } diff --git a/backend-spring/src/main/java/com/erp/controller/SubstituteController.java b/backend-spring/src/main/java/com/erp/controller/SubstituteController.java index e0ef5e32..5f5d8844 100644 --- a/backend-spring/src/main/java/com/erp/controller/SubstituteController.java +++ b/backend-spring/src/main/java/com/erp/controller/SubstituteController.java @@ -53,7 +53,7 @@ public class SubstituteController { @PathVariable("id") Long substituteId, @RequestAttribute("company_code") String companyCode, @RequestAttribute("role") String role) { - if (!"ADMIN".equals(role) && !"SUPER_ADMIN".equals(role)) { + if (!"ADMIN".equals(role) && !"COMPANY_ADMIN".equals(role) && !"SUPER_ADMIN".equals(role)) { return ResponseEntity.status(HttpStatus.FORBIDDEN) .body(ApiResponse.error("관리자만 조회할 수 있습니다.")); } diff --git a/backend-spring/src/main/java/com/erp/service/AdminService.java b/backend-spring/src/main/java/com/erp/service/AdminService.java index a83fc7a6..749ed51e 100644 --- a/backend-spring/src/main/java/com/erp/service/AdminService.java +++ b/backend-spring/src/main/java/com/erp/service/AdminService.java @@ -208,10 +208,17 @@ public class AdminService extends BaseService { } public void resetUserPassword(String userId) { - String defaultPw = passwordEncoder.encode("Welcome1!"); + resetUserPassword(userId, null); + } + + public void resetUserPassword(String userId, String newPassword) { + if (userId == null || userId.isBlank()) { + throw new IllegalArgumentException("user_id 는 필수입니다"); + } + String rawPw = (newPassword != null && !newPassword.isBlank()) ? newPassword : "Welcome1!"; Map params = new HashMap<>(); params.put("user_id", userId); - params.put("user_password", defaultPw); + params.put("user_password", passwordEncoder.encode(rawPw)); sqlSession.update("admin.updateUserPassword", params); } diff --git a/backend-spring/src/main/java/com/erp/service/DdlService.java b/backend-spring/src/main/java/com/erp/service/DdlService.java index 182174d5..543983c2 100644 --- a/backend-spring/src/main/java/com/erp/service/DdlService.java +++ b/backend-spring/src/main/java/com/erp/service/DdlService.java @@ -39,6 +39,12 @@ public class DdlService extends BaseService { "id", "created_date", "updated_date", "company_code" ); + /** 사용자가 신규 추가하는 컬럼에 허용되는 INPUT_TYPE 8종 (백엔드 백스톱) */ + private static final Set USER_SELECTABLE_INPUT_TYPES = Set.of( + "text", "number", "date", "code", "entity", + "numbering", "file", "image" + ); + public DdlService(JdbcTemplate jdbcTemplate, PlatformTransactionManager transactionManager) { this.jdbcTemplate = jdbcTemplate; this.transactionTemplate = new TransactionTemplate(transactionManager); @@ -140,6 +146,12 @@ public class DdlService extends BaseService { transactionTemplate.execute(status -> { jdbcTemplate.execute(ddlQuery); String inputType = convertToInputType(column); + if (!USER_SELECTABLE_INPUT_TYPES.contains(inputType)) { + throw new IllegalArgumentException( + "INPUT_TYPE 은 다음 8개 중 하나여야 합니다: " + USER_SELECTABLE_INPUT_TYPES + + " (받은 값: " + inputType + ")" + ); + } String detailSettings = column.containsKey("detail_settings") ? column.get("detail_settings").toString() : "{}"; Integer maxOrder = jdbcTemplate.queryForObject( @@ -408,10 +420,17 @@ public class DdlService extends BaseService { // 사용자 정의 컬럼 for (int i = 0; i < columns.size(); i++) { Map col = columns.get(i); + String inputType = convertToInputType(col); + if (!USER_SELECTABLE_INPUT_TYPES.contains(inputType)) { + throw new IllegalArgumentException( + "INPUT_TYPE 은 다음 8개 중 하나여야 합니다: " + USER_SELECTABLE_INPUT_TYPES + + " (받은 값: " + inputType + ")" + ); + } String detailSettings = col.containsKey("detail_settings") ? col.get("detail_settings").toString() : "{}"; saveColumnMeta(tableName, (String) col.get("name"), companyCode, - convertToInputType(col), detailSettings, i); + inputType, detailSettings, i); } } diff --git a/backend-spring/src/main/java/com/erp/service/SubstituteService.java b/backend-spring/src/main/java/com/erp/service/SubstituteService.java index 6e69e81a..91dd408f 100644 --- a/backend-spring/src/main/java/com/erp/service/SubstituteService.java +++ b/backend-spring/src/main/java/com/erp/service/SubstituteService.java @@ -239,7 +239,7 @@ public class SubstituteService extends BaseService { private void requireAdmin(Map params) { String role = (String) params.get("role"); - if (!"ADMIN".equals(role) && !"SUPER_ADMIN".equals(role)) { + if (!"ADMIN".equals(role) && !"COMPANY_ADMIN".equals(role) && !"SUPER_ADMIN".equals(role)) { throw new AccessDeniedException("관리자만 대무자를 지정/수정/해지할 수 있습니다."); } } diff --git a/backend-spring/src/main/java/com/erp/service/TableManagementService.java b/backend-spring/src/main/java/com/erp/service/TableManagementService.java index eb8d0d1e..832e3d4a 100644 --- a/backend-spring/src/main/java/com/erp/service/TableManagementService.java +++ b/backend-spring/src/main/java/com/erp/service/TableManagementService.java @@ -26,6 +26,12 @@ public class TableManagementService extends BaseService { private static final String NS = "tableManagement."; + /** 사용자가 직접 선택 가능한 INPUT_TYPE 8종 (INSERT/UPDATE-type 검증용) */ + private static final Set USER_SELECTABLE_INPUT_TYPES = Set.of( + "text", "number", "date", "code", "entity", + "numbering", "file", "image" + ); + // ────────────────────────────────────────────────── // 테이블 목록 // ────────────────────────────────────────────────── @@ -145,7 +151,9 @@ public class TableManagementService extends BaseService { Map settings, String companyCode) { ensureTableInLabels(tableName); - String inputType = normalizeInputType((String) settings.get("input_type")); + boolean inputTypeChanged = settings.containsKey("input_type"); + String ctx = inputTypeChanged ? "user-update-type" : "user-update-other"; + String inputType = normalizeInputType((String) settings.get("input_type"), ctx); Map params = new HashMap<>(); params.put("table_name", tableName); params.put("column_name", columnName); @@ -202,7 +210,7 @@ public class TableManagementService extends BaseService { public void updateColumnInputType(String tableName, String columnName, String inputType, String companyCode, Map detailSettings) { - String finalType = normalizeInputType(inputType); + String finalType = normalizeInputType(inputType, "user-update-type"); Map params = new HashMap<>(); params.put("table_name", tableName); params.put("column_name", columnName); @@ -853,7 +861,7 @@ public class TableManagementService extends BaseService { return name.replaceAll("[^a-zA-Z0-9_]", ""); } - /** "direct" / "auto" → "text" 변환 */ + /** "direct" / "auto" → "text" 변환 (legacy 호출처 보호 — system-normalize 동작) */ private String normalizeInputType(String inputType) { if ("direct".equals(inputType) || "auto".equals(inputType)) { log.warn("잘못된 inputType 값 감지: {} → 'text'로 변환", inputType); @@ -862,6 +870,24 @@ public class TableManagementService extends BaseService { return inputType != null ? inputType : "text"; } + /** + * context 에 따라 INPUT_TYPE 정규화 및 검증. + * @param context "user-insert" | "user-update-type" | "user-update-other" | "system-normalize" + */ + private String normalizeInputType(String value, String context) { + if ("user-insert".equals(context) || "user-update-type".equals(context)) { + if (value == null || !USER_SELECTABLE_INPUT_TYPES.contains(value)) { + throw new IllegalArgumentException( + "INPUT_TYPE 은 다음 8개 중 하나여야 합니다: " + USER_SELECTABLE_INPUT_TYPES + + " (받은 값: " + value + ")" + ); + } + return value; + } + // user-update-other / system-normalize: 기존 동작 그대로 + return normalizeInputType(value); + } + private String toJsonString(Object obj) { if (obj == null) return "{}"; if (obj instanceof String s) return s.isBlank() ? "{}" : s; diff --git a/backend-spring/src/main/resources/mapper/approval.xml b/backend-spring/src/main/resources/mapper/approval.xml index 91005bb9..aad08c1e 100644 --- a/backend-spring/src/main/resources/mapper/approval.xml +++ b/backend-spring/src/main/resources/mapper/approval.xml @@ -222,7 +222,7 @@ AND L.COMPANY_CODE = R.COMPANY_CODE ) - ORDER BY R.CREATED_DATE DESC + ORDER BY R.CREATED_AT DESC LIMIT #{page_limit} OFFSET #{page_offset} @@ -465,7 +465,7 @@ SELECT L.*, R.TITLE, R.TARGET_TABLE, R.TARGET_RECORD_ID, R.REQUESTER_NAME, R.REQUESTER_DEPT, - R.CREATED_DATE AS REQUEST_CREATED_DATE + R.CREATED_AT AS REQUEST_CREATED_DATE FROM APPROVAL_LINES L JOIN APPROVAL_REQUESTS R ON L.REQUEST_ID = R.REQUEST_ID AND L.COMPANY_CODE = R.COMPANY_CODE @@ -475,7 +475,7 @@ AND L.STATUS = 'pending' AND (L.COMPANY_CODE = #{company_code} OR L.COMPANY_CODE = '*') - ORDER BY R.CREATED_DATE ASC + ORDER BY R.CREATED_AT ASC