共计 3852 个字符,预计需要花费 10 分钟才能阅读完成。
前言
最近构建公司财务计税零碎, 因为我国税种较多, 且不同税种的金额梯度不同,计算公式也不, 并且因为业务须要, 时而须要正向计算税额 (税前推税后), 时而须要反向计算税额 (税后推税前). 构建架构时, 想基于一个接口, 且易于扩大。遂有了应用工厂 + 策略模式易于扩大的想法。
策略应用前剖析
(公司平安起因, 我就不写具体场景了, 这里以场景一,场景二,场景三... 来代替)
目前须要计税的有场景一 / 场景二 / 场景三。而每种场景又分为正向计算 / 逆向计算两种计算形式,故咱们须要应用的策略维度有:
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 办法就好了. 策略会帮忙咱们走具体逻辑.