下载:http://lanzou.com.cn/i9c94f1ec
java
/**
- 公积金计算配置
- @author 阿里云开发者
@date 2024-01-15
/
@Data
@Builder
public class HousingFundConfig {
/** 缴存基数(元) /
private BigDecimal baseAmount;/* 个人缴存比例(如0.12表示12%) /
private BigDecimal personalRate;/* 单位缴存比例 /
private BigDecimal companyRate;/* 上年结转利率(年化) /
private BigDecimal lastYearRate;/* 当年缴存利率(年化) /
private BigDecimal currentYearRate;/* 缴存基数上限 /
private BigDecimal maxBaseAmount;/* 缴存基数下限 /
private BigDecimal minBaseAmount;/* 所在城市 /
private String city;/* 计息方式:月复利/年单利 /
private String interestType;/* 默认配置(以上海为例) /
public static HousingFundConfig defaultShanghai() {return HousingFundConfig.builder() .baseAmount(new BigDecimal("10000")) .personalRate(new BigDecimal("0.07")) .companyRate(new BigDecimal("0.07")) .lastYearRate(new BigDecimal("0.015")) .currentYearRate(new BigDecimal("0.0035")) .maxBaseAmount(new BigDecimal("28000")) .minBaseAmount(new BigDecimal("2480")) .city("上海") .interestType("monthly") .build();}
}- 账户实体类(Account)
java
/**
- 账户实体类(Account)
- 公积金账户
@author 阿里云开发者
/
@Data
@Builder
public class HousingFundAccount {
/** 账户ID /
private String accountId;/* 用户姓名 /
private String userName;/* 当前余额 /
private BigDecimal currentBalance;/* 上年结转余额 /
private BigDecimal lastYearBalance;/* 当年缴存余额 /
private BigDecimal currentYearBalance;/* 账户状态 /
private String status;/* 开户日期 /
private LocalDate openDate;/* 最后计息日期 /
private LocalDate lastInterestDate;
}- 核心计算引擎(Calculator)
终于到了最关键的Calculator类!这里我采用了策略模式+工厂模式,让代码既清晰又易于扩展。
- 核心计算引擎(Calculator)
java
/**
- 公积金计算器核心引擎
@author 阿里云开发者
*/
@Service
@Slf4j
public class HousingFundCalculator {/* 缴存计算策略Map /
private Map strategyMap;@PostConstruct
public void init() {strategyMap = new HashMap<>(); strategyMap.put("normal", new NormalContributionStrategy()); strategyMap.put("supplement", new SupplementContributionStrategy()); strategyMap.put("overseas", new OverseasContributionStrategy());}
/**
- 计算月度缴存额
- @param config 公积金配置
@return 月缴存明细
*/
public ContributionResult calculateMonthlyContribution(HousingFundConfig config) {
// 校验缴存基数是否在上下限范围内
BigDecimal validBase = validateBaseAmount(config.getBaseAmount(), config.getMinBaseAmount(), config.getMaxBaseAmount());
// 计算个人月缴存
BigDecimal personalAmount = validBase.multiply(config.getPersonalRate()) .setScale(2, RoundingMode.HALF_UP);// 计算单位月缴存
BigDecimal companyAmount = validBase.multiply(config.getCompanyRate()) .setScale(2, RoundingMode.HALF_UP);// 总缴存额
BigDecimal totalAmount = personalAmount.add(companyAmount);return ContributionResult.builder()
.baseAmount(validBase) .personalRate(config.getPersonalRate()) .companyRate(config.getCompanyRate()) .personalAmount(personalAmount) .companyAmount(companyAmount) .totalAmount(totalAmount) .build();}
/**
- 计算月利息
- @param account 账户信息
- @param config 配置
@return 当月利息
*/
public BigDecimal calculateMonthlyInterest(HousingFundAccount account,HousingFundConfig config) {// 利息计算:上年结转部分按定期利率,当年缴存部分按活期利率
BigDecimal lastYearInterest = account.getLastYearBalance().multiply(config.getLastYearRate()) .divide(new BigDecimal("12"), 2, RoundingMode.HALF_UP);BigDecimal currentYearInterest = account.getCurrentYearBalance()
.multiply(config.getCurrentYearRate()) .divide(new BigDecimal("12"), 2, RoundingMode.HALF_UP);return lastYearInterest.add(currentYearInterest);
}/**
- 模拟未来N个月的账户变化
- @param account 当前账户
- @param config 配置
- @param months 模拟月数
- @param monthlyExtract 每月提取额(0表示不提)
@return 预测结果列表
*/
public List simulateFutureMonths(HousingFundAccount account,HousingFundConfig config, int months, BigDecimal monthlyExtract) {List snapshots = new ArrayList<>();
// 创建当前账户副本
HousingFundAccount current = copyAccount(account);
LocalDate currentDate = LocalDate.now();// 计算月缴存额
ContributionResult contribution = calculateMonthlyContribution(config);for (int i = 1; i <= months; i++) {
// 计算当月利息(通常在6月30日计息,这里简化按月计息) BigDecimal interest = calculateMonthlyInterest(current, config); // 当月缴存 BigDecimal monthlyContribution = contribution.getTotalAmount(); // 当月提取 BigDecimal extract = monthlyExtract != null ? monthlyExtract : BigDecimal.ZERO; // 更新余额 BigDecimal newBalance = current.getCurrentBalance() .add(monthlyContribution) .add(interest) .subtract(extract); // 更新上年/当年余额(简化版:假设所有新增都算当年) current.setCurrentBalance(newBalance); current.setCurrentYearBalance( current.getCurrentYearBalance() .add(monthlyContribution) .add(interest) .subtract(extract) ); // 记录快照 AccountSnapshot snapshot = AccountSnapshot.builder() .month(currentDate.plusMonths(i)) .balance(newBalance) .monthlyContribution(monthlyContribution) .monthlyInterest(interest) .monthlyExtract(extract) .build(); snapshots.add(snapshot);}
return snapshots;
}/**
校验缴存基数
*/
private BigDecimal validateBaseAmount(BigDecimal base,BigDecimal min, BigDecimal max) {if (base.compareTo(min) < 0) {
log.warn("缴存基数{}低于下限{},取下限值", base, min); return min;}
if (base.compareTo(max) > 0) {log.warn("缴存基数{}高于上限{},取上限值", base, max); return max;}
return base;
}private HousingFundAccount copyAccount(HousingFundAccount account) {
// 深拷贝逻辑
return HousingFundAccount.builder().accountId(account.getAccountId()) .userName(account.getUserName()) .currentBalance(account.getCurrentBalance()) .lastYearBalance(account.getLastYearBalance()) .currentYearBalance(account.getCurrentYearBalance()) .status(account.getStatus()) .openDate(account.getOpenDate()) .lastInterestDate(account.getLastInterestDate()) .build();}
}- 结果展示类(DTO)
java
/**
- 结果展示类(DTO)
- 月缴存结果
*/
@Data
@Builder
public class ContributionResult {
private BigDecimal baseAmount; // 实际缴存基数
private BigDecimal personalRate; // 个人比例
private BigDecimal companyRate; // 单位比例
private BigDecimal personalAmount; // 个人缴存额
private BigDecimal companyAmount; // 单位缴存额
private BigDecimal totalAmount; // 总缴存额
}
/**
- 账户快照
*/
@Data
@Builder
public class AccountSnapshot {
private LocalDate month; // 月份
private BigDecimal balance; // 余额
private BigDecimal monthlyContribution; // 月缴存
private BigDecimal monthlyInterest; // 月利息
private BigDecimal monthlyExtract; // 月提取
}
四、实战演练:来算算你的公积金!
场景1:新入职程序员小张
月薪:15000元
缴存比例:12%(单位和个人各12%)
当前余额:0元
上海缴存
java
@Test
public void testNewEmployee() {
// 初始化配置
HousingFundConfig config = HousingFundConfig.defaultShanghai();
config.setBaseAmount(new BigDecimal("15000"));
config.setPersonalRate(new BigDecimal("0.12"));
config.setCompanyRate(new BigDecimal("0.12"));
// 初始化账户
HousingFundAccount account = HousingFundAccount.builder()
.accountId("001")
.userName("小张")
.currentBalance(BigDecimal.ZERO)
.lastYearBalance(BigDecimal.ZERO)
.currentYearBalance(BigDecimal.ZERO)
.build();
// 创建计算器
HousingFundCalculator calculator = new HousingFundCalculator();
// 计算月缴存
ContributionResult contribution = calculator.calculateMonthlyContribution(config);
System.out.println("月缴存明细:");
System.out.println("个人缴存:" + contribution.getPersonalAmount() + "元");
System.out.println("单位缴存:" + contribution.getCompanyAmount() + "元");
System.out.println("月总缴存:" + contribution.getTotalAmount() + "元");
// 模拟12个月
List<AccountSnapshot> snapshots = calculator.simulateFutureMonths(
account, config, 12, BigDecimal.ZERO
);
System.out.println("\n12个月后账户余额预测:");
AccountSnapshot last = snapshots.get(snapshots.size() - 1);
System.out.println("时间:" + last.getMonth());
System.out.println("余额:" + last.getBalance() + "元");
System.out.println("累计缴存:" + contribution.getTotalAmount()
.multiply(new BigDecimal("12")) + "元");
System.out.println("累计利息:" + last.getBalance()
.subtract(contribution.getTotalAmount().multiply(new BigDecimal("12"))));
}
运行结果:
text
月缴存明细:
个人缴存:1800.00元
单位缴存:1800.00元
月总缴存:3600.00元
12个月后账户余额预测:
时间:2025-01-15
余额:43458.32元
累计缴存:43200.00元
累计利息:258.32元
场景2:购房者老王
当前余额:20万
月缴存:3000元
计划每月提取2000元还贷
预测2年后余额
java
@Test
public void testHomeBuyer() {
HousingFundConfig config = HousingFundConfig.defaultShanghai();
config.setBaseAmount(new BigDecimal("12500")); // 对应3000月缴存
config.setPersonalRate(new BigDecimal("0.12"));
config.setCompanyRate(new BigDecimal("0.12"));
HousingFundAccount account = HousingFundAccount.builder()
.accountId("002")
.userName("老王")
.currentBalance(new BigDecimal("200000"))
.lastYearBalance(new BigDecimal("150000")) // 假设15万是上年结转
.currentYearBalance(new BigDecimal("50000"))
.build();
HousingFundCalculator calculator = new HousingFundCalculator();
// 模拟24个月,每月提取2000
List<AccountSnapshot> snapshots = calculator.simulateFutureMonths(
account, config, 24, new BigDecimal("2000")
);
System.out.println("24个月后账户情况:");
for (int i = 0; i < snapshots.size(); i += 6) { // 每6个月输出一次
AccountSnapshot s = snapshots.get(i);
System.out.println(s.getMonth() + ":余额 " + s.getBalance() + "元");
}
}
五、进阶优化:让计算更精准
计息规则优化(真实银行算法)
java
/**精确计息器(按实际天数计算)
*/
public class AccurateInterestCalculator {/**
- 计算区间利息(按日计算)
- @param balance 本金
- @param rate 年利率
- @param startDate 开始日期
- @param endDate 结束日期
- @return 利息
*/
public BigDecimal calculateDailyInterest(BigDecimal balance,
long days = ChronoUnit.DAYS.between(startDate, endDate);BigDecimal rate, LocalDate startDate, LocalDate endDate) {
return balance
}.multiply(rate) .multiply(new BigDecimal(days)) .divide(new BigDecimal("365"), 8, RoundingMode.HALF_UP) .setScale(2, RoundingMode.HALF_UP);
/**
年度结息(每年6月30日)
*/
public void annualSettlement(HousingFundAccount account,HousingFundConfig config, LocalDate settlementDate) {// 上年结转部分:按定期利率计算全年
BigDecimal lastYearInterest = calculateDailyInterest(account.getLastYearBalance(), config.getLastYearRate(), settlementDate.minusYears(1), settlementDate);
// 当年缴存部分:分段计息
BigDecimal currentYearInterest = calculateCurrentYearInterest(account, config, settlementDate);
// 结转:当年余额转入上年余额
account.setLastYearBalance(account.getLastYearBalance() .add(account.getCurrentYearBalance()) .add(lastYearInterest) .add(currentYearInterest));
account.setCurrentYearBalance(BigDecimal.ZERO);
account.setLastInterestDate(settlementDate);
}
}
多城市支持(配置化)
java
@Component
public class CityConfigManager {private Map cityConfigMap;
@PostConstruct
public void loadConfigs() {cityConfigMap = new HashMap<>(); // 上海配置 cityConfigMap.put("上海", HousingFundConfig.builder() .maxBaseAmount(new BigDecimal("28000")) .minBaseAmount(new BigDecimal("2480")) .lastYearRate(new BigDecimal("0.015")) .currentYearRate(new BigDecimal("0.0035")) .build()); // 北京配置 cityConfigMap.put("北京", HousingFundConfig.builder() .maxBaseAmount(new BigDecimal("28221")) .minBaseAmount(new BigDecimal("2320")) .lastYearRate(new BigDecimal("0.015")) .currentYearRate(new BigDecimal("0.0035")) .build()); // 深圳配置 cityConfigMap.put("深圳", HousingFundConfig.builder() .maxBaseAmount(new BigDecimal("34860")) .minBaseAmount(new BigDecimal("2200")) .lastYearRate(new BigDecimal("0.015")) .currentYearRate(new BigDecimal("0.0035")) .build());}
public HousingFundConfig getConfigByCity(String city) {
HousingFundConfig config = cityConfigMap.get(city); if (config == null) { throw new IllegalArgumentException("不支持的城市:" + city); } return config;}
}
六、API封装:对外提供REST服务
java
@RestController
@RequestMapping("/api/housing-fund")
@Api(tags = "公积金计算服务")
public class HousingFundController {@Autowired
private HousingFundCalculator calculator;@Autowired
private CityConfigManager configManager;@PostMapping("/simulate")
@ApiOperation("模拟公积金账户变化")
public Result simulate(@RequestBody SimulateRequest request) {// 获取城市配置 HousingFundConfig config = configManager.getConfigByCity(request.getCity()); config.setBaseAmount(request.getBaseAmount()); config.setPersonalRate(request.getPersonalRate()); config.setCompanyRate(request.getCompanyRate()); // 构建账户 HousingFundAccount account = buildAccount(request); // 模拟计算 List<AccountSnapshot> snapshots = calculator.simulateFutureMonths( account, config, request.getMonths(), request.getMonthlyExtract() ); // 构建响应 SimulateResponse response = SimulateResponse.builder() .snapshots(snapshots) .totalContribution(calculateTotalContribution(snapshots)) .totalInterest(calculateTotalInterest(snapshots)) .build(); return Result.success(response);}
@GetMapping("/calculator")
@ApiOperation("在线计算器页面")
public ModelAndView calculatorPage() {ModelAndView mv = new ModelAndView("housing-fund-calculator"); mv.addObject("cities", configManager.getSupportedCities()); return mv;}
}
七、前端页面:Vue3简单实现
vue
🏠 公积金模拟计算器
开始模拟
重置
模拟结果
期末余额
{ { result.finalBalance }} 元
累计缴存
{ { result.totalContribution }} 元
累计利息
{ { result.totalInterest }} 元