前言
最近构建公司财务计税系统,由于我国税种较多,且不同税种的金额梯度不同,计算公式也不,并且由于业务需要,时而需要正向计算税额(税前推税后),时而需要反向计算税额(税后推税前).构建架构时,想基于一个接口,且易于扩展。遂有了使用工厂+策略模式易于扩展的想法。
策略使用前分析
(公司安全原因,我就不写具体场景了,这里以场景一,场景二,场景三...来代替)
目前需要计税的有场景一/场景二/场景三。而每种场景又分为正向计算/逆向计算两种计算方式,故我们需要使用的策略维度有:
1.场景一 + 正向计算
2.场景一 + 逆向计算
3.场景二 + 正向计算
4.场景二 + 逆向计算
5.场景三 + 正向计算
6.场景四 + 逆向计算
我们可以在Spring容器启动的时候,将这几种case加载spring的时候就存在某个地方,这样我们需要哪种场景,传递具体code,取出我们需要的strategy就好了。那么很明显,我们可以在工厂里面放一个hashmap用来存储具体的strategy,如下:
/**
* @author LiuLiang (iamcrawler@sina.com)
* @since 2020-11-16 11:52
*/
@Component
public class TaxCalculateStrategyFactory {
private static Map<String, TaxCalculateStrategy> strategyMap = new ConcurrentHashMap();
/**
* 获取实现策略 * * @param model
* @return
*/
public static TaxCalculateStrategy getInvokeStrategyByModel(String model) {
return strategyMap.get(model);
}
public static void register(String str, TaxCalculateStrategy iTaxStrategyService) {
if (StringUtils.isEmpty(str) || null == iTaxStrategyService) {
return;
}
strategyMap.put(str, iTaxStrategyService);
}
}
有了这个工厂,我们需要在spring容器启动的时候,就把具体strategy放到map里面,当然这个时候方式有很多种,比如在构造的时候放进去等等,当然我个人认为,此动作不应该和具体的bean有太大的耦合关系,故我使用的方式是实现spring的 InitializingBean ,并 实现自己的策略父类如下:
策略父类:
public interface TaxCalculateStrategy {
/**
* @param monthlySumIncome 月总收入
* @param monthlySumTax 月总(已)缴纳税额
* @param currentIncome 当前笔收入
* @return
*/
Long calculate(Long monthlySumIncome, Long monthlySumTax, Long currentIncome);
}
策略具体实现类
/**
* 劳务税正算逻辑 * * @author LiuLiang
* @since 2020-11-16 11:28
*/@Service
public class LaborTaxForwardCalculateStrategy implements TaxCalculateStrategy, InitializingBean {
@Override
public Long calculate(Long monthlySumIncome, Long monthlySumTax, Long currentIncome) {
//税前总金额
Long preTaxIncome = monthlySumIncome + monthlySumTax + currentIncome;
//税后总金额
Long afterTaxIncome = getAfterTaxIncome(preTaxIncome);
//总税额
Long totalTax = preTaxIncome - afterTaxIncome;
//本次税额
Long currentTax = totalTax - monthlySumTax;
return currentTax;
}
/**
* 劳务税 正向计算公式 * 应纳税所得额 = 劳务报酬(少于4000元) - 800元 * <p>
* 应纳税所得额 = 劳务报酬(超过4000元) × (1 - 20%)
* <p>
* 应纳税额 = 应纳税所得额 × 适用税率 - 速算扣除数
* <p>
* 说明:
* <p>
* 1、劳务报酬所得在800元以下的,不用缴纳个人所得税;
* <p>
* 2、劳务报酬所得大于800元且没有超过4000元,可减除800元的扣除费用;
* <p>
* 3、劳务报酬所得超过4000元的,可减除劳务报酬收入20%的扣除费用;
* <p>
* 个税计算器税率表
* 级数 应纳税所得额 税率(%) 速算扣除数 * 1 不超过20,000元 20% 0 * 2 超过20,000元至50,000元的部分 30% 2,000 * 3 超过50,000元的部分 40% 7,000 * * @param preTaxIncome 税前金额 单位:分
* @return
*/
public Long getAfterTaxIncome(Long preTaxIncome) {
Long afterTaxIncome = preTaxIncome;
//应纳税所得额
BigDecimal taxableIncome = BigDecimal.ZERO;
if (!(preTaxIncome > TaxCenterConstant.RMB_4000.longValue())) {
taxableIncome = BigDecimal.valueOf(preTaxIncome).subtract(TaxCenterConstant.RMB_800);
} else {
taxableIncome = BigDecimal.valueOf(preTaxIncome).multiply(TaxCenterConstant.TAXABLE_INCOME_RATE_8);
}
if (taxableIncome.compareTo(BigDecimal.ZERO) < 0) {
return afterTaxIncome;
} else if (taxableIncome.compareTo(TaxCenterConstant.RMB_20000) < 0) {
return preTaxIncome - taxableIncome.multiply(TaxCenterConstant.WITHHOLDING_RATE_20).longValue();
} else if (taxableIncome.compareTo(TaxCenterConstant.RMB_50000) < 0 && taxableIncome.compareTo(TaxCenterConstant.RMB_20000) > 0) {
return preTaxIncome - taxableIncome.multiply(TaxCenterConstant.WITHHOLDING_RATE_30).subtract(TaxCenterConstant.RMB_2000).longValue();
} else if (taxableIncome.compareTo(TaxCenterConstant.RMB_50000) > 0) {
return preTaxIncome - taxableIncome.multiply(TaxCenterConstant.WITHHOLDING_RATE_40).subtract(TaxCenterConstant.RMB_7000).longValue();
}
return afterTaxIncome;
}
@Override
public void afterPropertiesSet() throws Exception {
TaxCalculateStrategyFactory.register("LABOR-FORWARD", this);
}
可以看到afterPropertiesSet 方法以及把具体的code register 到静态的hashmap里面去了,具体的实现,在重新父类的calculate() 方法里面。同理其他的几个策略,就不贴具体代码了
怎么使用?
public BigDecimal taxCalculate(Long currentIncomeAmount, Long monthlyIncome, Long monthlyTax) {
//按照既定策略计算税额
TaxCalculateStrategy strategy = TaxCalculateStrategyFactory.getInvokeStrategyByModel(
SceneCodeIncomeEnum.SOW_GRASS.getTaxType() + "-" + SceneCodeIncomeEnum.SOW_GRASS.getModel());
Long currentTax = strategy.calculate(monthlyIncome, monthlyTax, currentIncomeAmount);
return BigDecimal.valueOf(currentTax);
}
可以看到,我们拿到具体的策略后,调用calculate方法就好了.策略会帮助我们走具体逻辑.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。