package com.mydreamjob.salarycalculator; import com.mydreamjob.salarycalculator.dto.SalaryCalculationOverrideRequest; import com.mydreamjob.salarycalculator.dto.SalaryCalculationRequest; import org.springframework.stereotype.Component; import java.math.BigDecimal; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.HexFormat; @Component public class SalaryRequestFingerprint { public String fingerprint(SalaryCalculationRequest request) { StringBuilder canonical = new StringBuilder(); append(canonical, normalize(request.getCityCode())); append(canonical, normalize(request.getExpectedPolicyVersion())); append(canonical, decimal(request.getGrossMonthlySalary())); append(canonical, String.valueOf(request.getMonthIndex() == null ? 1 : request.getMonthIndex())); append(canonical, decimal(request.getSpecialAdditionalDeduction() == null ? BigDecimal.ZERO : request.getSpecialAdditionalDeduction())); SalaryCalculationOverrideRequest overrides = request.getOverrides(); if (overrides == null) { for (int i = 0; i < 7; i++) { append(canonical, ""); } } else { append(canonical, decimal(overrides.getSocialSecurityBase())); append(canonical, decimal(overrides.getHousingFundBase())); append(canonical, decimal(overrides.getPensionRate())); append(canonical, decimal(overrides.getMedicalRate())); append(canonical, decimal(overrides.getUnemploymentRate())); append(canonical, decimal(overrides.getHousingFundRate())); append(canonical, decimal(overrides.getSeriousIllnessMonthlyFee())); } return sha256(canonical.toString()); } private void append(StringBuilder builder, String value) { builder.append(value.length()).append(':').append(value).append('|'); } private String normalize(String value) { return value == null ? "" : value.trim(); } private String decimal(BigDecimal value) { if (value == null) { return ""; } BigDecimal normalized = value.stripTrailingZeros(); if (normalized.scale() < 0) { normalized = normalized.setScale(0); } return normalized.toPlainString(); } private String sha256(String value) { try { MessageDigest digest = MessageDigest.getInstance("SHA-256"); return HexFormat.of().formatHex(digest.digest(value.getBytes(StandardCharsets.UTF_8))); } catch (NoSuchAlgorithmException ex) { throw new IllegalStateException("SHA-256 is unavailable", ex); } } }