前言

在报表数据处理中,Excel公式领有弱小而多样的性能,广泛应用于各个业务畛域。无论是投资收益计算、财务报表编制还是保险收益估算,Excel公式都扮演着不可或缺的角色。传统的做法是间接依赖Excel来实现简单的业务逻辑,并生成相应的Excel文件。因而只需在预设地位输出相应参数,Excel公式即可被激活,迅速计算并出现后果。正因如此,在这类场景中,企业积攒了大量用于计算的Excel文件,它们曾经成为了无价的财产。

然而,传统的Excel文件形式存在难以治理和数据不平安的毛病。为了解决这些问题,能够采纳B/S架构+Excel组件库的形式。

本文将以个人所得税的计算为例,应用React+Spring Boot+GcExcel来实现。首先筹备好Excel文件,依照国家税务总局提供的个税计算页面进行创立。

个人所得税的支出类型有8种:

  • 工资薪金所得
  • 年终奖所得
  • 劳务报酬所得
  • 个体工商户、生产经营所得
  • 酬劳所得
  • 偶尔所得
  • 利息、股息、红利所得
  • 财产转让所得

其中,工资薪金所得最为简单,包含社会保险和专项扣除。每种类型的计税形式都不同,为了便于了解,咱们为每个类型创立了一个工作表进行计算。

以下是筹备好的Excel文件,其中蓝色局部为须要输出参数的单元格,其余单元格将主动计算。

实现筹备工作后,上面开始前后端工程的搭建。

实际

前端 React

创立React工程

新建一个文件夹,如TaxCalculator,进入文件夹,在资源管理器的地址栏里输出cmd,而后回车,关上命令行窗口。应用上面的代码创立名为client-app的react app。

npx create-react-app salary-client

进入刚创立的salary-client文件夹,应用IDE,比方VisualStudio Code关上文件夹。

界面局部

个人所得税波及的支出类型一共有8种,其中(“酬劳所得”,“偶尔所得”,“利息、股息、红利所得”,“财产转让所得”)四种的计算形式靠近,UI布局类似,借助React的component个性,最终须要提供5种表单界面。

如下图所示:

为了让UI看起来更好看一些,能够先引入一个UI框架,这里咱们应用了MUI。

npm install @mui/material @emotion/react @emotion/styled

首先,更新Src/App.js的代码,其中增加了DarkMode的Theme, 代码如下:

import './App.css';import { ThemeProvider } from '@emotion/react';import { createTheme } from '@mui/material';import { FormContainer } from './Component/FormContainer';const darkTheme = createTheme({  palette: {    mode: 'dark',  },});function App() {  return (    <ThemeProvider theme={darkTheme}>      <div className="App-header">        <h2>个人所得税计算器</h2>        <FormContainer></FormContainer>      </div>    </ThemeProvider>  );}export default App;

能够看到,App.js中援用了FormContainer,下来增加 ./Component/FormContainer.js。

FormContainer次要是提供一个Selector,让用户抉择支出类型,依据抉择的类型渲染不同的组件。

import React, { useState } from 'react';import { SalaryIncome } from "./SalaryIncome"import { NativeSelect, FormControl } from '@mui/material';import { BounsIncome } from './BounsIncome';import { CommercialIncome } from './CommercialIncome';import { LaborIncome } from './LaborIncome';import { OtherIncome } from './OtherIncome';export const FormContainer = () => {    const [calcType, setCalcType] = useState("工资薪金所得");    const GetIncomeControl = () => {        switch (calcType) {            case "工资薪金所得":                return <SalaryIncome calcType={calcType}></SalaryIncome>;            case "年终奖所得":                return <BounsIncome calcType={calcType}></BounsIncome>;            case "劳务报酬所得":                return <LaborIncome calcType={calcType}></LaborIncome>;            case "个体工商户、生产经营所得":                return <CommercialIncome calcType={calcType}></CommercialIncome>;            default:                return <OtherIncome calcType={calcType}></OtherIncome>;        }    }    return (        <div style={{ width: "60vw", marginTop: "5vh" }}>            <FormControl fullWidth sx={{ marginBottom: 2 }}>                <NativeSelect labelId="demo-simple-select-label" id="demo-simple-select"                    value={calcType} label="类型" onChange={e => setCalcType(e.target.value)}                    >                    <option value="工资薪金所得">工资薪金所得</option>                    <option value="年终奖所得">年终奖所得</option>                    <option Item value="劳务报酬所得">劳务报酬所得</option>                    <option value="个体工商户、生产经营所得">个体工商户、生产经营所得</option>                    <option value="酬劳所得">酬劳所得</option>                    <option value="偶尔所得">偶尔所得</option>                    <option value="利息、股息、红利所得">利息、股息、红利所得</option>                </NativeSelect>            </FormControl>            {GetIncomeControl()}        </div>);}

例如:\<SalaryIncome calcType={calcType}\>\</SalaryIncome\>; 同时会将calcType传递进去。

接下来,别离创立几个xxxIncome组件。

1.工资薪金所得 SalaryIncome.js

import React, { useState } from 'react';import { TextField, Button, Stack } from '@mui/material';import axios from 'axios';export const SalaryIncome = (props) => {    const [income, setIncome] = useState("");    const [insurance, setInsurance] = useState("");    const [childEdu, setChildEdu] = useState("");    const [selfEdu, setSelfEdu] = useState("");    const [treatment, setTreatment] = useState("");    const [loans, setLoans] = useState("");    const [rent, setRent] = useState("");    const [elder, setElder] = useState("");    const [taxableIncome, setTaxableIncome] = useState("");    const [taxRate, setTaxRate] = useState("");    const [deduction, setDeduction] = useState("");    const [tax, setTax] = useState("");    const [takeHomeSalary, setTakeHomeSalary] = useState("");    async function calculateTax(event) {        event.preventDefault();        let res = await axios.post("api/calcPersonTax", {            calcType: props.calcType,            income: income,            insurance: insurance,            childEdu: childEdu,            selfEdu: selfEdu,            treatment: treatment,            loans: loans,            rent: rent,            elder: elder,        });        if (res != null) {            let data = res.data;            setTaxableIncome(data.taxableIncome);            setTaxRate(data.taxRate);            setDeduction(data.deduction);            setTax(data.tax);            setTakeHomeSalary(data.takeHomeSalary);        }    }    function reset(event) {        event.preventDefault();        setIncome("");        setInsurance("");        setChildEdu("");        setSelfEdu("");        setTreatment("");        setLoans("");        setRent("");        setElder("");        setTaxableIncome("");        setTaxRate("");        setDeduction("");        setTax("");        setTakeHomeSalary("");    }    return (        <div>            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>                <TextField type="text" variant='outlined' color='primary'                    label="税前工资" onChange={e => setIncome(e.target.value)}                    value={income} fullWidth required size="small"/>                <TextField type="text" variant='outlined' color='secondary'                    label="社会保险/公积金" onChange={e => setInsurance(e.target.value)}                    value={insurance} fullWidth size="small"/>            </Stack>            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>                <TextField type="text" variant='outlined' color='secondary'                    label="子女教育专项扣除" onChange={e => setChildEdu(e.target.value)}                    value={childEdu} fullWidth size="small"/>                <TextField type="text" variant='outlined' color='secondary'                    label="持续教育专项扣除" onChange={e => setSelfEdu(e.target.value)}                    value={selfEdu} fullWidth size="small"/>            </Stack>            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>                <TextField type="text" variant='outlined' color='secondary'                    label="大病医疗专项扣除" onChange={e => setTreatment(e.target.value)}                    value={treatment} fullWidth size="small"/>                <TextField type="text" variant='outlined' color='secondary'                    label="住房贷款利息专项扣除" onChange={e => setLoans(e.target.value)}                    value={loans} fullWidth size="small"/>            </Stack>            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>                <TextField type="text" variant='outlined' color='secondary'                    label="住房租金专项扣除" onChange={e => setRent(e.target.value)}                    value={rent} fullWidth size="small"/>                <TextField type="text" variant='outlined' color='secondary'                    label="奉养老人专项扣除" onChange={e => setElder(e.target.value)}                    value={elder} fullWidth size="small"/>            </Stack>            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>                <TextField type="text" variant='outlined' color='secondary'                    label="起征点" value="5000 元/月" fullWidth disabled size="small"/>            </Stack>            <hr></hr>            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>                <Button variant="outlined" color="primary" onClick={calculateTax} fullWidth size="large">计算</Button>                <Button variant="outlined" color="secondary" onClick={reset} fullWidth size="large">重置</Button>            </Stack>            <hr></hr>            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>                <TextField type="text" variant='outlined' color='secondary'                    label="应征税所得额" value={taxableIncome} fullWidth disabled size="small"/>                <TextField type="text" variant='outlined' color='secondary'                    label="税率" value={taxRate} fullWidth disabled size="small"/>            </Stack>            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>                <TextField type="text" variant='outlined' color='secondary'                    label="速算扣除数" value={deduction} fullWidth disabled size="small"/>                <TextField type="text" variant='outlined' color='secondary'                    label="应纳税额" value={tax} fullWidth disabled size="small"/>            </Stack>            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>                <TextField type="text" variant='outlined' color='secondary'                    label="税后工资" value={takeHomeSalary} fullWidth disabled size="small"/>            </Stack>        </div>    )}

2.年终奖金所得 BounsIncome.js

import React, { useState } from 'react';import { TextField, Button, Stack } from '@mui/material';import axios from 'axios';export const BounsIncome = (props) => {    const [income, setIncome] = useState("");    const [taxableIncome, setTaxableIncome] = useState("");    const [taxRate, setTaxRate] = useState("");    const [deduction, setDeduction] = useState("");    const [monthlyWage, setMonthlyWage] = useState("");    const [tax, setTax] = useState("");    const [takeHomeSalary, setTakeHomeSalary] = useState("");    async function calculateTax(event) {        event.preventDefault();        let res = await axios.post("api/calcPersonTax", {            calcType: props.calcType,            income: income,        });        if (res != null) {            let data = res.data;            setTaxableIncome(data.taxableIncome);            setTaxRate(data.taxRate);            setDeduction(data.deduction);            setMonthlyWage(data.monthlyWage);            setTax(data.tax);            setTakeHomeSalary(data.takeHomeSalary);        }    }    function reset(event) {        event.preventDefault();        setIncome("");        setTaxableIncome("");        setTaxRate("");        setDeduction("");        setMonthlyWage("");        setTax("");        setTakeHomeSalary("");    }    return (        <div>            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>                <TextField type="text" variant='outlined' color='primary' size="small"                    label="税前工资" onChange={e => setIncome(e.target.value)}                    value={income} fullWidth required />            </Stack>            <hr></hr>            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>                <Button variant="outlined" color="primary" onClick={calculateTax} fullWidth size="large">计算</Button>                <Button variant="outlined" color="secondary" onClick={reset} fullWidth size="large">重置</Button>            </Stack>            <hr></hr>            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>                <TextField type="text" variant='outlined' color='secondary' size="small"                    label="应征税所得额" value={taxableIncome} fullWidth disabled />                <TextField type="text" variant='outlined' color='secondary' size="small"                    label="税率" value={taxRate} fullWidth disabled />            </Stack>            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>                <TextField type="text" variant='outlined' color='secondary' size="small"                    label="速算扣除数" value={deduction} fullWidth disabled />                <TextField type="text" variant='outlined' color='secondary' size="small"                    label="均匀每月工资" value={monthlyWage} fullWidth disabled />            </Stack>            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>                <TextField type="text" variant='outlined' color='secondary' size="small"                    label="应纳税额" value={tax} fullWidth disabled />                <TextField type="text" variant='outlined' color='secondary' size="small"                    label="税后工资" value={takeHomeSalary} fullWidth disabled />            </Stack>        </div>    )}

3.劳务报酬所得 LaborIncome.js

import React, { useState } from 'react';import { TextField, Button, Stack } from '@mui/material';import axios from 'axios';export const LaborIncome = (props) => {    const [income, setIncome] = useState("");    const [taxableIncome, setTaxableIncome] = useState("");    const [taxRate, setTaxRate] = useState("");    const [deduction, setDeduction] = useState("");    const [nonTaxablePart, setNonTaxablePart] = useState("");    const [tax, setTax] = useState("");    const [takeHomeSalary, setTakeHomeSalary] = useState("");    async function calculateTax(event) {        event.preventDefault();        let res = await axios.post("api/calcPersonTax", {            calcType: props.calcType,            income: income,        });        if (res != null) {            let data = res.data;            setTaxableIncome(data.taxableIncome);            setTaxRate(data.taxRate);            setDeduction(data.deduction);            setNonTaxablePart(data.nonTaxablePart);            setTax(data.tax);            setTakeHomeSalary(data.takeHomeSalary);        }    }    function reset(event) {        event.preventDefault();        setIncome("");        setTaxableIncome("");        setTaxRate("");        setDeduction("");        setNonTaxablePart("");        setTax("");        setTakeHomeSalary("");    }    return (        <div>            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>                <TextField type="text" variant='outlined' color='primary' size="small"                    label="税前工资" onChange={e => setIncome(e.target.value)}                    value={income} fullWidth required />            </Stack>            <hr></hr>            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>                <Button variant="outlined" color="primary" onClick={calculateTax} fullWidth size="large">计算</Button>                <Button variant="outlined" color="secondary" onClick={reset} fullWidth size="large">重置</Button>            </Stack>            <hr></hr>            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>                <TextField type="text" variant='outlined' color='secondary' size="small"                    label="应征税所得额" value={taxableIncome} fullWidth disabled />                <TextField type="text" variant='outlined' color='secondary' size="small"                    label="税率" value={taxRate} fullWidth disabled />            </Stack>            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>                <TextField type="text" variant='outlined' color='secondary' size="small"                    label="速算扣除数" value={deduction} fullWidth disabled />                <TextField type="text" variant='outlined' color='secondary' size="small"                    label="减除费用" value={nonTaxablePart} fullWidth disabled />            </Stack>            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>                <TextField type="text" variant='outlined' color='secondary' size="small"                    label="应纳税额" value={tax} fullWidth disabled />                <TextField type="text" variant='outlined' color='secondary' size="small"                    label="税后工资" value={takeHomeSalary} fullWidth disabled />            </Stack>        </div>    )}

4.个体工商户、生产经营所得 CommercialIncome.js

import React, { useState } from 'react';import { TextField, Button, Stack } from '@mui/material';import axios from 'axios';export const CommercialIncome = (props) => {    const [income, setIncome] = useState("");    const [taxableIncome, setTaxableIncome] = useState("");    const [taxRate, setTaxRate] = useState("");    const [deduction, setDeduction] = useState("");    const [tax, setTax] = useState("");    const [takeHomeSalary, setTakeHomeSalary] = useState("");    async function calculateTax(event) {        event.preventDefault();        let res = await axios.post("api/calcPersonTax", {            calcType: props.calcType,            income: income,        });        if (res != null) {            let data = res.data;            setTaxableIncome(data.taxableIncome);            setTaxRate(data.taxRate);            setDeduction(data.deduction);            setTax(data.tax);            setTakeHomeSalary(data.takeHomeSalary);        }    }    function reset(event) {        event.preventDefault();        setIncome("");        setTaxableIncome("");        setTaxRate("");        setDeduction("");        setTax("");        setTakeHomeSalary("");    }    return (        <div>            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>                <TextField type="text" variant='outlined' color='primary' size="small"                    label="税前工资" onChange={e => setIncome(e.target.value)}                    value={income} fullWidth required />            </Stack>            <hr></hr>            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>                <Button variant="outlined" color="primary" onClick={calculateTax} fullWidth size="large">计算</Button>                <Button variant="outlined" color="secondary" onClick={reset} fullWidth size="large">重置</Button>            </Stack>            <hr></hr>            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>                <TextField type="text" variant='outlined' color='secondary' size="small"                    label="应征税所得额" value={taxableIncome} fullWidth disabled />                <TextField type="text" variant='outlined' color='secondary' size="small"                    label="税率" value={taxRate} fullWidth disabled />            </Stack>            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>                <TextField type="text" variant='outlined' color='secondary' size="small"                    label="速算扣除数" value={deduction} fullWidth disabled />                <TextField type="text" variant='outlined' color='secondary' size="small"                    label="应纳税额" value={tax} fullWidth disabled />            </Stack>            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>                <TextField type="text" variant='outlined' color='secondary' size="small"                    label="税后工资" value={takeHomeSalary} fullWidth disabled />            </Stack>        </div>    )}

5.余下四种类型 OtherIncome.js

import React, { useState } from 'react';import { TextField, Button, Stack } from '@mui/material';import axios from 'axios';export const OtherIncome = (props) => {    const [income, setIncome] = useState("");    const [taxableIncome, setTaxableIncome] = useState("");    const [taxRate, setTaxRate] = useState("");    const [tax, setTax] = useState("");    const [takeHomeSalary, setTakeHomeSalary] = useState("");    async function calculateTax(event) {        event.preventDefault();        let res = await axios.post("api/calcPersonTax", {            calcType: props.calcType,            income: income,        });        if (res != null) {            let data = res.data;            setTaxableIncome(data.taxableIncome);            setTaxRate(data.taxRate);            setTax(data.tax);            setTakeHomeSalary(data.takeHomeSalary);        }    }    function reset(event) {        event.preventDefault();        setIncome("");        setTaxableIncome("");        setTaxRate("");        setTax("");        setTakeHomeSalary("");    }    return (        <div>            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>                <TextField type="text" variant='outlined' color='primary' size="small"                    label={props.calcType} onChange={e => setIncome(e.target.value)}                    value={income} fullWidth required />            </Stack>            <hr></hr>            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>                <Button variant="outlined" color="primary" onClick={calculateTax} fullWidth size="large">计算</Button>                <Button variant="outlined" color="secondary" onClick={reset} fullWidth size="large">重置</Button>            </Stack>            <hr></hr>            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>                <TextField type="text" variant='outlined' color='secondary' size="small"                    label="应征税所得额" value={taxableIncome} fullWidth disabled />                <TextField type="text" variant='outlined' color='secondary' size="small"                    label="税率" value={taxRate} fullWidth disabled />            </Stack>            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>                <TextField type="text" variant='outlined' color='secondary' size="small"                    label="应纳税额" value={tax} fullWidth disabled />                <TextField type="text" variant='outlined' color='secondary' size="small"                    label="税后工资" value={takeHomeSalary} fullWidth disabled />            </Stack>        </div>    )}

此时,实现UI局部后,能够尝试运行起来,成果如下:

//通过代码运行React appnpm start

能够试着填一些数据,然而当咱们点击计算时会报错,这是因为服务端还没有筹备好。

前端申请局部

相熟Axios的同学能够跳过这部分,后面的代码里,曾经给出了Axois发送申请的代码。

能够看到无论是哪一种类型的组件,申请都发送到了雷同的url("api/calcPersonTax"),以SalaryIncome为例,代码如下:

async function calculateTax(event) {        event.preventDefault();        let res = await axios.post("api/calcPersonTax", {            calcType: props.calcType,            income: income,            insurance: insurance,            childEdu: childEdu,            selfEdu: selfEdu,            treatment: treatment,            loans: loans,            rent: rent,            elder: elder,        });        if (res != null) {            let data = res.data;            setTaxableIncome(data.taxableIncome);            setTaxRate(data.taxRate);            setDeduction(data.deduction);            setTax(data.tax);            setTakeHomeSalary(data.takeHomeSalary);        }    }

能够看到,整个申请变得非常简单,次要是把state的值取出来,通过post申请发送到服务端,而后依据返回值,把数据从新设给state,这样就实现UI数据的更新了。

配置申请转发中间件

咱们在申请时拜访的是绝对地址,React自身有一个nodeJS,默认的端口是3000,而Spring Boot的默认端口是8080。前端间接拜访会有跨域的问题,因而咱们要做一个代理的配置。

在src文件夹上面增加文件,名为setupProxy.js,代码如下:

const { createProxyMiddleware } = require('http-proxy-middleware');module.exports = function(app) {  app.use(    '/api',    createProxyMiddleware({      target: 'http://localhost:8080',      changeOrigin: true,    })  );};

服务端 Spring Boot

创立工程及增加依赖

应用IDEA创立一个Spring Boot工程,如果应用的是社区(community)版本,不能间接创立Spring Boot我的项目,那能够先创立一个空我的项目,idea创立project的过程,就跳过了,这里咱们以创立了一个gradle我的项目为例。

plugins {    id 'org.springframework.boot' version '3.0.0'    id 'io.spring.dependency-management' version '1.1.0'    id 'java'    id 'war'}group = 'org.example'version = '1.0-SNAPSHOT'repositories {    mavenCentral()}dependencies {    implementation 'org.springframework.boot:spring-boot-starter-web'    implementation 'com.grapecity.documents:gcexcel:6.2.0'    implementation 'javax.json:javax.json-api:1.1.4'    providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'    testImplementation('org.springframework.boot:spring-boot-starter-test')}test {    useJUnitPlatform()}

在dependencies 中,咱们除了依赖Spring Boot之外,还增加了GcExcel的依赖,前面导出时会用到GcExcel,目前的版本是6.2.0。

增加API

在Application类上,增加属性 @RequestMapping("/api").,并增加 calcPersonTax API。

@Spring BootApplication@RestController@RequestMapping("/api")public class SalaryTaxCalculator {    public static void main(String[] args) {        SpringApplication.run(SalaryTaxCalculator.class, args);    }    @PostMapping("/calcPersonTax")    public CalcResult calcTax(@RequestBody CalcParameter par) {        Workbook workbook = new Workbook();        workbook.open(GetResourcePath());        return CalcInternal(workbook, par);    }        private String GetResourcePath(){        return Objects.requireNonNull(SalaryTaxCalculator.class.getClassLoader().getResource("PersonalTaxCalcEngine.xlsx")).getPath();    }        private CalcResult CalcInternal(Workbook workbook, CalcParameter par) {        //todo    }}

能够看到在CalcInternal办法内,咱们应用GcExcel,依据calcType来判断应用哪一个sheet来进行计算。对不同Sheet只须要通过GcExcel设值,并从特定的格子里取值即可。

同时,咱们还须要创立两个类,CalcParameter和CalcResult。CalcParameter用于从request中把post的data解析进去,CalcResult用于在response中返回的数据。

CalcParameter:

public class CalcParameter {    public String calcType;    public double income;    public double insurance;    public double childEdu;    public double selfEdu;    public double treatment;    public double loans;    public double rent;    public double elder;}

CalcResult:

public class CalcResult {    public double taxableIncome;    public double taxRate;    public double deduction;    public double tax;    public double takeHomeSalary;    public double monthlyWage;    public double nonTaxablePart;}

应用GcExcel实现公式计算

后面咱们定义了 CalcInternal,在 CalcInternal 中,咱们须要应用GcExcel来实现公式计算。

GcExcel的公式计算是主动实现的,咱们应用workbook关上Excel文件后,只须要set相干的value。之后在取值时,GcExcel会主动计算响应公式的值。

private CalcResult CalcInternal(Workbook workbook, CalcParameter par) {        var result = new CalcResult();        var sheet = workbook.getWorksheets().get(par.calcType);        switch (par.calcType) {            case "工资薪金所得" -> {                sheet.getRange("B1").setValue(par.income);                sheet.getRange("D1").setValue(par.insurance);                sheet.getRange("B2").setValue(par.childEdu);                sheet.getRange("D2").setValue(par.selfEdu);                sheet.getRange("B3").setValue(par.treatment);                sheet.getRange("D3").setValue(par.loans);                sheet.getRange("B4").setValue(par.rent);                sheet.getRange("D4").setValue(par.elder);                result.taxableIncome = (double) sheet.getRange("B9").getValue();                result.taxRate = (double) sheet.getRange("D9").getValue();                result.deduction = (double) sheet.getRange("B10").getValue();                result.tax = (double) sheet.getRange("D10").getValue();                result.takeHomeSalary = (double) sheet.getRange("B11").getValue();            }            case "年终奖所得" -> {                sheet.getRange("B1").setValue(par.income);                result.taxableIncome = (double) sheet.getRange("B3").getValue();                result.taxRate = (double) sheet.getRange("D3").getValue();                result.deduction = (double) sheet.getRange("B4").getValue();                result.monthlyWage = (double) sheet.getRange("D4").getValue();                result.tax = (double) sheet.getRange("B5").getValue();                result.takeHomeSalary = (double) sheet.getRange("D5").getValue();            }            case "劳务报酬所得" -> {                sheet.getRange("B1").setValue(par.income);                result.taxableIncome = (double) sheet.getRange("B3").getValue();                result.taxRate = (double) sheet.getRange("D3").getValue();                result.deduction = (double) sheet.getRange("B4").getValue();                result.nonTaxablePart = (double) sheet.getRange("D4").getValue();                result.tax = (double) sheet.getRange("B5").getValue();                result.takeHomeSalary = (double) sheet.getRange("D5").getValue();            }            case "个体工商户、生产经营所得" -> {                sheet.getRange("B1").setValue(par.income);                result.taxableIncome = (double) sheet.getRange("B3").getValue();                result.taxRate = (double) sheet.getRange("D3").getValue();                result.deduction = (double) sheet.getRange("B4").getValue();                result.tax = (double) sheet.getRange("D4").getValue();                result.takeHomeSalary = (double) sheet.getRange("B5").getValue();            }            default -> {                sheet.getRange("B1").setValue(par.income);                result.taxableIncome = (double) sheet.getRange("B3").getValue();                result.taxRate = (double) sheet.getRange("D3").getValue();                result.tax = (double) sheet.getRange("B4").getValue();                result.takeHomeSalary = (double) sheet.getRange("D4").getValue();            }        }        return result;    }

这样就实现了服务端的代码。

最终成果

咱们能够应用工资薪金所得试验一下,能够看到数据被计算出来了。因为目标是为了分享服务端公式计算的计划,所以计算的后果是否正确,就不做粗疏思考。

总结

个税计算的场景并不简单,次要是通过Excel实现公式计算即可,在服务端应用GcExcel能够大幅度降低前后端的开发难度,零碎的搭建过程能够齐全不须要思考计算的逻辑。

在理论的公式计算场景中,可能往往会比个税计算的场景简单,借助GcExcel这样Excel组件库,能够很容易的把已有的Excel文件迁徙到线上,进步工作效率。

另外,本文中分享的代码并不是最符合实际工作中的要求,读者还能够从以下角度去优化本人的代码。

  1. 支出类型能够抽成枚举,这样保护和应用起来更容易。
  2. 目前每一个react组件里的冗余度还不低,还能够持续形象组件,防止反复写代码。
  3. 在服务端,因为公式计算的逻辑是不会变的,在理论场景中,也有可能同一时间要加载复数个Excel文件,能够思考把workbook常驻内存,来进步性能。


扩大链接:

高级SQL剖析函数-如何用窗口函数进行排名计算

3D模型+BI剖析,打造全新的交互式3D可视化大屏开发计划

React + Springboot + Quartz,从0实现Excel报表自动化