package com.zioinfo.hrm.admin; import com.zioinfo.hrm.auth.HrmUser; import com.zioinfo.hrm.common.ApiResponse; import com.zioinfo.hrm.common.AuthSupport; import lombok.RequiredArgsConstructor; import org.springframework.security.core.Authentication; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.web.bind.annotation.*; import java.util.HashMap; import java.util.List; import java.util.Map; @RestController @RequestMapping("/api/hrm/admin") @RequiredArgsConstructor public class AdminController { private final AdminMapper adminMapper; private final PasswordEncoder passwordEncoder; @GetMapping("/users") public ApiResponse> users() { List users = adminMapper.findAllUsers(); users.forEach(u -> u.setPasswordHash(null)); // 비밀번호 해시 미노출 return ApiResponse.ok(users); } @PostMapping("/users") public ApiResponse createUser(@RequestBody HrmUser user) { user.setPasswordHash(passwordEncoder.encode(user.getPasswordHash())); user.setActive(true); adminMapper.insertUser(user); return ApiResponse.ok(null); } @PutMapping("/users/{id}") public ApiResponse updateUser(@PathVariable Long id, @RequestBody HrmUser user) { user.setId(id); if (user.getPasswordHash() != null && !user.getPasswordHash().isEmpty()) { user.setPasswordHash(passwordEncoder.encode(user.getPasswordHash())); } adminMapper.updateUser(user); return ApiResponse.ok(null); } @PatchMapping("/users/{id}/active") public ApiResponse toggleActive(@PathVariable Long id, @RequestParam boolean active) { adminMapper.updateUserActive(id, active); return ApiResponse.ok(null); } /** * 관리자 OTP 초기화(SUPERADMIN). 대상 사용자의 OTP 시크릿을 폐기하고 등록을 해제한다. * 사용자는 다음 로그인 시 OTP_SETUP(QR 재등록) 플로우를 탄다. 시크릿은 응답/로그에 노출하지 않는다. */ @PostMapping("/users/{id}/otp-reset") public ApiResponse resetOtp(@PathVariable Long id, Authentication auth) { HrmUser user = adminMapper.findUserById(id); if (user == null) { throw new RuntimeException("ERR-HRM-404: 대상 사용자를 찾을 수 없습니다"); } adminMapper.clearOtp(id); Map log = new HashMap<>(); log.put("actor", AuthSupport.actor(auth)); log.put("action", "USER_OTP_RESET"); log.put("targetType", "auth.otp"); log.put("targetId", user.getUsername()); log.put("detail", "OTP 초기화(다음 로그인 재등록)"); log.put("ipAddr", null); adminMapper.insertAuditLog(log); user.setPasswordHash(null); // 비밀번호 해시 미노출 return ApiResponse.ok(user); } @GetMapping("/audit") public ApiResponse> audit( @RequestParam(required = false) String actor, @RequestParam(required = false) String action, @RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "50") int size) { int offset = (page - 1) * size; List> rows = adminMapper.findAuditLogs(actor, action, offset, size); long total = adminMapper.countAuditLogs(actor, action); Map r = new HashMap<>(); r.put("items", rows); r.put("total", total); return ApiResponse.ok(r); } @GetMapping("/settings") public ApiResponse>> settings() { return ApiResponse.ok(adminMapper.findSettings()); } @PutMapping("/settings/{key}") public ApiResponse upsertSetting(@PathVariable String key, @RequestBody Map body, Authentication auth) { body.put("key", key); body.put("updatedBy", AuthSupport.actor(auth)); adminMapper.upsertSetting(body); return ApiResponse.ok(null); } }