|
|
|
|
@ -21,6 +21,7 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
|
|
|
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
|
|
|
|
import lombok.RequiredArgsConstructor;
|
|
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
|
|
import org.springframework.http.HttpStatus;
|
|
|
|
|
import org.springframework.http.ResponseEntity;
|
|
|
|
|
import org.springframework.stereotype.Controller;
|
|
|
|
|
import org.springframework.ui.Model;
|
|
|
|
|
@ -510,5 +511,266 @@ public class CrdnLevyPrvntcController {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 시가표준액 계산 API
|
|
|
|
|
* 중요로직: 건축물과세시가에서 1,000원 미만을 절사하여 시가표준액을 계산합니다.
|
|
|
|
|
*
|
|
|
|
|
* @param bdstTxtnMprc 건축물과세시가
|
|
|
|
|
* @return 시가표준액 계산 결과
|
|
|
|
|
*/
|
|
|
|
|
@Operation(summary = "시가표준액 계산", description = "건축물과세시가에서 1,000원 미만 절사하여 시가표준액을 계산합니다.")
|
|
|
|
|
@ApiResponses(value = {
|
|
|
|
|
@ApiResponse(responseCode = "200", description = "계산 성공"),
|
|
|
|
|
@ApiResponse(responseCode = "400", description = "잘못된 숫자 형식"),
|
|
|
|
|
@ApiResponse(responseCode = "500", description = "서버 오류")
|
|
|
|
|
})
|
|
|
|
|
@PostMapping("/calculateStandardMarketPrice.ajax")
|
|
|
|
|
@ResponseBody
|
|
|
|
|
public ResponseEntity<?> calculateStandardMarketPrice(
|
|
|
|
|
@Parameter(description = "건축물과세시가") @RequestParam String bdstTxtnMprc) {
|
|
|
|
|
|
|
|
|
|
Map<String, Object> result = new HashMap<>();
|
|
|
|
|
try {
|
|
|
|
|
BigDecimal bdstTxtnMprcDecimal = new BigDecimal(bdstTxtnMprc);
|
|
|
|
|
|
|
|
|
|
// 중요로직: 시가표준액 = 건축물과세시가에서 1,000원 미만 절사
|
|
|
|
|
BigDecimal mprcStdAmt = bdstTxtnMprcDecimal
|
|
|
|
|
.divide(new BigDecimal("1000"), 0, RoundingMode.DOWN)
|
|
|
|
|
.multiply(new BigDecimal("1000"));
|
|
|
|
|
|
|
|
|
|
result.put("success", true);
|
|
|
|
|
result.put("mprcStdAmt", mprcStdAmt.toPlainString());
|
|
|
|
|
return ApiResponseUtil.success(result, "시가표준액이 계산되었습니다.");
|
|
|
|
|
|
|
|
|
|
} catch (NumberFormatException e) {
|
|
|
|
|
log.error("숫자 형식 변환 오류", e);
|
|
|
|
|
result.put("success", false);
|
|
|
|
|
result.put("message", "잘못된 숫자 형식입니다.");
|
|
|
|
|
return ResponseEntity.badRequest().body(result);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
log.error("시가표준액 계산 중 오류 발생", e);
|
|
|
|
|
result.put("success", false);
|
|
|
|
|
result.put("message", "시가표준액 계산 중 오류가 발생했습니다.");
|
|
|
|
|
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 산정액 및 부과총액 계산 API
|
|
|
|
|
* 중요로직: 시가표준액, 위반면적, 가감산시행령률, 산정률, 산정률2를 이용하여 산정액과 부과총액을 계산합니다.
|
|
|
|
|
*
|
|
|
|
|
* @param mprcStdAmt 시가표준액
|
|
|
|
|
* @param vltnArea 위반면적
|
|
|
|
|
* @param adsbmtnEnfcRt 가감산시행령률
|
|
|
|
|
* @param cmpttnRtRate 산정률 비율값
|
|
|
|
|
* @param cmpttnRt2Rate 산정률2 비율값
|
|
|
|
|
* @return 산정액 및 부과총액 계산 결과
|
|
|
|
|
*/
|
|
|
|
|
@Operation(summary = "산정액 및 부과총액 계산", description = "시가표준액 × 위반면적 × 가감산시행령률 × 산정률 × 산정률2로 산정액을 계산하고, 1의 자리 절사하여 부과총액을 계산합니다.")
|
|
|
|
|
@ApiResponses(value = {
|
|
|
|
|
@ApiResponse(responseCode = "200", description = "계산 성공"),
|
|
|
|
|
@ApiResponse(responseCode = "400", description = "잘못된 숫자 형식"),
|
|
|
|
|
@ApiResponse(responseCode = "500", description = "서버 오류")
|
|
|
|
|
})
|
|
|
|
|
@PostMapping("/calculateLevyAmount.ajax")
|
|
|
|
|
@ResponseBody
|
|
|
|
|
public ResponseEntity<?> calculateLevyAmount(
|
|
|
|
|
@Parameter(description = "시가표준액") @RequestParam String mprcStdAmt,
|
|
|
|
|
@Parameter(description = "위반면적") @RequestParam String vltnArea,
|
|
|
|
|
@Parameter(description = "가감산시행령률") @RequestParam String adsbmtnEnfcRt,
|
|
|
|
|
@Parameter(description = "산정률 비율값") @RequestParam String cmpttnRtRate,
|
|
|
|
|
@Parameter(description = "산정률2 비율값") @RequestParam String cmpttnRt2Rate) {
|
|
|
|
|
|
|
|
|
|
Map<String, Object> result = new HashMap<>();
|
|
|
|
|
try {
|
|
|
|
|
BigDecimal mprcStdAmtDecimal = new BigDecimal(mprcStdAmt);
|
|
|
|
|
BigDecimal vltnAreaDecimal = new BigDecimal(vltnArea);
|
|
|
|
|
BigDecimal adsbmtnEnfcRtDecimal = new BigDecimal(adsbmtnEnfcRt);
|
|
|
|
|
BigDecimal cmpttnRtRateDecimal = new BigDecimal(cmpttnRtRate);
|
|
|
|
|
BigDecimal cmpttnRt2RateDecimal = new BigDecimal(cmpttnRt2Rate);
|
|
|
|
|
|
|
|
|
|
// 중요로직: 산정액 = 시가표준액 × 위반면적 × (가감산시행령률 ÷ 100) × 산정률 × 산정률2
|
|
|
|
|
BigDecimal cmpttnAmt = mprcStdAmtDecimal
|
|
|
|
|
.multiply(vltnAreaDecimal)
|
|
|
|
|
.multiply(adsbmtnEnfcRtDecimal.divide(new BigDecimal("100"), 10, RoundingMode.HALF_UP))
|
|
|
|
|
.multiply(cmpttnRtRateDecimal)
|
|
|
|
|
.multiply(cmpttnRt2RateDecimal)
|
|
|
|
|
.setScale(0, RoundingMode.DOWN); // 소수점 버림
|
|
|
|
|
|
|
|
|
|
// 중요로직: 부과총액 = 산정액의 1의 자리 절사 (10원 단위 버림)
|
|
|
|
|
BigDecimal levyWholAmt = cmpttnAmt
|
|
|
|
|
.divide(new BigDecimal("10"), 0, RoundingMode.DOWN)
|
|
|
|
|
.multiply(new BigDecimal("10"));
|
|
|
|
|
|
|
|
|
|
result.put("success", true);
|
|
|
|
|
result.put("cmpttnAmt", cmpttnAmt.toPlainString());
|
|
|
|
|
result.put("levyWholAmt", levyWholAmt.toPlainString());
|
|
|
|
|
return ApiResponseUtil.success(result, "산정액 및 부과총액이 계산되었습니다.");
|
|
|
|
|
|
|
|
|
|
} catch (NumberFormatException e) {
|
|
|
|
|
log.error("숫자 형식 변환 오류", e);
|
|
|
|
|
result.put("success", false);
|
|
|
|
|
result.put("message", "잘못된 숫자 형식입니다.");
|
|
|
|
|
return ResponseEntity.badRequest().body(result);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
log.error("산정액 및 부과총액 계산 중 오류 발생", e);
|
|
|
|
|
result.put("success", false);
|
|
|
|
|
result.put("message", "산정액 및 부과총액 계산 중 오류가 발생했습니다.");
|
|
|
|
|
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 가감산시행령률 계산 API
|
|
|
|
|
* 중요로직: 기본 100%에서 가산율 또는 감산율을 적용하여 가감산시행령률을 계산합니다.
|
|
|
|
|
*
|
|
|
|
|
* @param baseRate 기본율 (보통 100)
|
|
|
|
|
* @param adtnRt 가산율 (선택적)
|
|
|
|
|
* @param sbtrRt 감산율 (선택적)
|
|
|
|
|
* @return 가감산시행령률 계산 결과
|
|
|
|
|
*/
|
|
|
|
|
@Operation(summary = "가감산시행령률 계산", description = "기본율 + 가산율 - 감산율로 가감산시행령률을 계산합니다.")
|
|
|
|
|
@ApiResponses(value = {
|
|
|
|
|
@ApiResponse(responseCode = "200", description = "계산 성공"),
|
|
|
|
|
@ApiResponse(responseCode = "400", description = "잘못된 숫자 형식"),
|
|
|
|
|
@ApiResponse(responseCode = "500", description = "서버 오류")
|
|
|
|
|
})
|
|
|
|
|
@PostMapping("/calculateAdsbmtnEnfcRt.ajax")
|
|
|
|
|
@ResponseBody
|
|
|
|
|
public ResponseEntity<?> calculateAdsbmtnEnfcRt(
|
|
|
|
|
@Parameter(description = "기본율") @RequestParam(defaultValue = "100") String baseRate,
|
|
|
|
|
@Parameter(description = "가산율") @RequestParam(required = false, defaultValue = "0") String adtnRt,
|
|
|
|
|
@Parameter(description = "감산율") @RequestParam(required = false, defaultValue = "0") String sbtrRt) {
|
|
|
|
|
|
|
|
|
|
Map<String, Object> result = new HashMap<>();
|
|
|
|
|
try {
|
|
|
|
|
BigDecimal baseRateDecimal = new BigDecimal(baseRate);
|
|
|
|
|
BigDecimal adtnRtDecimal = new BigDecimal(adtnRt);
|
|
|
|
|
BigDecimal sbtrRtDecimal = new BigDecimal(sbtrRt);
|
|
|
|
|
|
|
|
|
|
// 중요로직: 가감산시행령률 = 기본율 + 가산율 - 감산율
|
|
|
|
|
BigDecimal adsbmtnEnfcRt = baseRateDecimal
|
|
|
|
|
.add(adtnRtDecimal)
|
|
|
|
|
.subtract(sbtrRtDecimal);
|
|
|
|
|
|
|
|
|
|
// 범위 검증 (0 ~ 1000% 제한)
|
|
|
|
|
if (adsbmtnEnfcRt.compareTo(BigDecimal.ZERO) < 0) {
|
|
|
|
|
adsbmtnEnfcRt = BigDecimal.ZERO;
|
|
|
|
|
} else if (adsbmtnEnfcRt.compareTo(new BigDecimal("1000")) > 0) {
|
|
|
|
|
adsbmtnEnfcRt = new BigDecimal("1000");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result.put("success", true);
|
|
|
|
|
result.put("adsbmtnEnfcRt", adsbmtnEnfcRt.toPlainString());
|
|
|
|
|
return ApiResponseUtil.success(result, "가감산시행령률이 계산되었습니다.");
|
|
|
|
|
|
|
|
|
|
} catch (NumberFormatException e) {
|
|
|
|
|
log.error("숫자 형식 변환 오류", e);
|
|
|
|
|
result.put("success", false);
|
|
|
|
|
result.put("message", "잘못된 숫자 형식입니다.");
|
|
|
|
|
return ResponseEntity.badRequest().body(result);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
log.error("가감산시행령률 계산 중 오류 발생", e);
|
|
|
|
|
result.put("success", false);
|
|
|
|
|
result.put("message", "가감산시행령률 계산 중 오류가 발생했습니다.");
|
|
|
|
|
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 실시간 계산용 통합 API (디바운싱 + 캐싱 지원)
|
|
|
|
|
* 중요로직: 입력값들을 받아서 시가표준액, 산정액, 부과총액을 한번에 계산하여 반환합니다.
|
|
|
|
|
* 캐싱을 통해 동일한 입력값에 대한 중복 계산을 방지합니다.
|
|
|
|
|
*
|
|
|
|
|
* @param bdstTxtnMprc 건축물과세시가
|
|
|
|
|
* @param vltnArea 위반면적
|
|
|
|
|
* @param adsbmtnEnfcRt 가감산시행령률
|
|
|
|
|
* @param cmpttnRtRate 산정률 비율값
|
|
|
|
|
* @param cmpttnRt2Rate 산정률2 비율값 (커스텀 입력용)
|
|
|
|
|
* @return 모든 계산 결과를 포함한 응답
|
|
|
|
|
*/
|
|
|
|
|
@Operation(summary = "실시간 계산용 통합 API", description = "실시간 입력을 위한 모든 계산을 한번에 처리합니다. 디바운싱과 캐싱을 지원합니다.")
|
|
|
|
|
@ApiResponses(value = {
|
|
|
|
|
@ApiResponse(responseCode = "200", description = "계산 성공"),
|
|
|
|
|
@ApiResponse(responseCode = "400", description = "잘못된 숫자 형식"),
|
|
|
|
|
@ApiResponse(responseCode = "500", description = "서버 오류")
|
|
|
|
|
})
|
|
|
|
|
@PostMapping("/calculateRealtime.ajax")
|
|
|
|
|
@ResponseBody
|
|
|
|
|
public ResponseEntity<?> calculateRealtime(
|
|
|
|
|
@Parameter(description = "건축물과세시가") @RequestParam String bdstTxtnMprc,
|
|
|
|
|
@Parameter(description = "위반면적") @RequestParam String vltnArea,
|
|
|
|
|
@Parameter(description = "가감산시행령률") @RequestParam String adsbmtnEnfcRt,
|
|
|
|
|
@Parameter(description = "산정률 비율값") @RequestParam String cmpttnRtRate,
|
|
|
|
|
@Parameter(description = "산정률2 비율값") @RequestParam String cmpttnRt2Rate) {
|
|
|
|
|
|
|
|
|
|
Map<String, Object> result = new HashMap<>();
|
|
|
|
|
try {
|
|
|
|
|
// 중요로직: 입력값 검증 및 BigDecimal 변환
|
|
|
|
|
BigDecimal bdstTxtnMprcDecimal = new BigDecimal(bdstTxtnMprc);
|
|
|
|
|
BigDecimal vltnAreaDecimal = new BigDecimal(vltnArea);
|
|
|
|
|
BigDecimal adsbmtnEnfcRtDecimal = new BigDecimal(adsbmtnEnfcRt);
|
|
|
|
|
BigDecimal cmpttnRtRateDecimal = new BigDecimal(cmpttnRtRate);
|
|
|
|
|
BigDecimal cmpttnRt2RateDecimal = new BigDecimal(cmpttnRt2Rate);
|
|
|
|
|
|
|
|
|
|
// 1단계: 시가표준액 계산 (1,000원 미만 절사)
|
|
|
|
|
BigDecimal mprcStdAmt = bdstTxtnMprcDecimal
|
|
|
|
|
.divide(new BigDecimal("1000"), 0, java.math.RoundingMode.DOWN)
|
|
|
|
|
.multiply(new BigDecimal("1000"));
|
|
|
|
|
|
|
|
|
|
// 2단계: 산정액 계산 (시가표준액 × 위반면적 × 가감산시행령률(%) × 산정률(비율) × 산정률2(비율))
|
|
|
|
|
BigDecimal cmpttnAmt = mprcStdAmt
|
|
|
|
|
.multiply(vltnAreaDecimal)
|
|
|
|
|
.multiply(adsbmtnEnfcRtDecimal.divide(new BigDecimal("100"), 10, java.math.RoundingMode.HALF_UP))
|
|
|
|
|
.multiply(cmpttnRtRateDecimal)
|
|
|
|
|
.multiply(cmpttnRt2RateDecimal)
|
|
|
|
|
.setScale(0, java.math.RoundingMode.DOWN);
|
|
|
|
|
|
|
|
|
|
// 3단계: 부과총액 계산 (10원 단위 절사)
|
|
|
|
|
BigDecimal levyWholAmt = cmpttnAmt
|
|
|
|
|
.divide(new BigDecimal("10"), 0, java.math.RoundingMode.DOWN)
|
|
|
|
|
.multiply(new BigDecimal("10"));
|
|
|
|
|
|
|
|
|
|
// 중요로직: 결과 데이터 구성 (캐싱을 위한 구조화된 응답)
|
|
|
|
|
result.put("success", true);
|
|
|
|
|
result.put("message", "실시간 계산이 성공적으로 완료되었습니다.");
|
|
|
|
|
|
|
|
|
|
// 계산 결과
|
|
|
|
|
Map<String, Object> calculations = new HashMap<>();
|
|
|
|
|
calculations.put("mprcStdAmt", mprcStdAmt); // 시가표준액
|
|
|
|
|
calculations.put("cmpttnAmt", cmpttnAmt); // 산정액
|
|
|
|
|
calculations.put("levyWholAmt", levyWholAmt); // 부과총액
|
|
|
|
|
|
|
|
|
|
// 표시용 포맷팅된 값
|
|
|
|
|
Map<String, String> displayValues = new HashMap<>();
|
|
|
|
|
displayValues.put("mprcStdAmtDisplay", String.format("%,d", mprcStdAmt.longValue()) + " 원");
|
|
|
|
|
displayValues.put("cmpttnAmtDisplay", String.format("%,d", cmpttnAmt.longValue()) + " 원");
|
|
|
|
|
displayValues.put("levyWholAmtDisplay", String.format("%,d", levyWholAmt.longValue()) + " 원");
|
|
|
|
|
|
|
|
|
|
result.put("calculations", calculations);
|
|
|
|
|
result.put("displayValues", displayValues);
|
|
|
|
|
|
|
|
|
|
// 캐싱을 위한 입력 파라미터 해시값 (프론트엔드에서 사용)
|
|
|
|
|
String cacheKey = String.format("%s_%s_%s_%s_%s",
|
|
|
|
|
bdstTxtnMprc, vltnArea, adsbmtnEnfcRt, cmpttnRtRate, cmpttnRt2Rate);
|
|
|
|
|
result.put("cacheKey", cacheKey.hashCode());
|
|
|
|
|
|
|
|
|
|
log.debug("실시간 계산 완료 - 시가표준액: {}, 산정액: {}, 부과총액: {}",
|
|
|
|
|
mprcStdAmt, cmpttnAmt, levyWholAmt);
|
|
|
|
|
|
|
|
|
|
return ResponseEntity.ok(result);
|
|
|
|
|
|
|
|
|
|
} catch (NumberFormatException e) {
|
|
|
|
|
log.error("실시간 계산 중 숫자 형식 변환 오류", e);
|
|
|
|
|
result.put("success", false);
|
|
|
|
|
result.put("message", "잘못된 숫자 형식입니다.");
|
|
|
|
|
return ResponseEntity.badRequest().body(result);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
log.error("실시간 계산 중 오류 발생", e);
|
|
|
|
|
result.put("success", false);
|
|
|
|
|
result.put("message", "실시간 계산 중 오류가 발생했습니다.");
|
|
|
|
|
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|