基于Servlet框架的前后端交互web计算器

​​​​​​基于Servlet框架的前后端交互web计算器

目录

​​​​​​基于Servlet框架的前后端交互web计算器

git仓库链接和代码规范链接

PSP表格

成品展示

1、基础计算器功能

2、利率计算器功能

设计实现过程

代码说明

前端

后端

心路历程和收获

这个作业属于哪个课程<班级的链接>
这个作业要求在哪里<作业要求的链接>
这个作业的目标完成体现体现前后端分离的web计算器
  • git仓库链接和代码规范链接

前端102101422_calculator_frontend · master · 102101422肖宇龙 / 基于Servlet的前后端交互计算器 · GitCode
后端102101422_calculator_backend · master · 102101422肖宇龙 / 基于Servlet的前后端交互计算器 · GitCode
  • PSP表格

PSPPersonal Software Process Stages预估耗时(分钟)实际耗时(分钟)
Planning计划6060
• Estimate• 估计这个任务需要多少时间4560
Development开发19002100
• Analysis• 需求分析 (包括学习新技术)600850
• Design Spec• 生成设计文档3060
• Design Review• 设计复审3045
• Coding Standard• 代码规范 (为目前的开发制定合适的规范)3020
• Design• 具体设计6075
• Coding• 具体编码600700
• Code Review• 代码复审12090
• Test• 测试(自我测试,修改代码,提交修改)3045
Reporting报告6065
• Test Repor• 测试报告3025
• Size Measurement• 计算工作量2015
• Postmortem & Process Improvement Plan• 事后总结, 并提出过程改进计划1025
合计20202225
  • 成品展示

1、基础计算器功能

  • 功能1:加减乘除、取余、括号运算
    • 基础+,-,* ,/,%,()运算,运算顺序正确,结果正确,并将输入的字符串和结果存储到后端json文件中

  • 功能2:清零回退
    • 实现字符串输入的清零和回退功能

  • 功能3:错误提示
    • 在第一个功能正常的情况下增加非法输入提示,例如:0不能作为除数,括号不匹配等

出现括号不匹配等错误会报ERROR,但是遇到除数为0错误会报Infinity

  • 功能4:读取历史记录
    • 能用返回上一个计算结果,并且查看历史记录读取最后十个字符串式子和对应的计算结果。

  • 附加功能1:科学计算器
    • 计算根号、三角函数、log、幂运算等附加功能

  • 附加功能2:页面原型设计
    • 仿照百度计算器进行原型设计,页面设计复现与现有的计算器页面相同。

比较完整复刻了百度计算器的风格以及按键等布局,符合现有计算器页面。

2、利率计算器功能

  • 计算存款、贷款利息
    • 实现利率计算器,数据库中存储利率表(可随时修改利率),对应利率如下图所示。用两个输入框分别读取前端的输入数据
    • 输入存款/贷款金额,
    • 存/贷时长(单位:年)。

1. 存款利息

存款利率计算器提供了两种计算方式,活期和定期。存款期限是一个下拉框的选择,用户根据实际情况选择存款期限,计算器将根据不同的存款方式进行计算得到所得利息和本息总计。

2. 贷款利息

贷款利息计算器可以通过存款利息计算器跳转,其使用方式与存款利息计算器相似,但是可以用户自行填写贷款期限。

  • 设计实现过程

        本次设计的web计算器是基于Servlet框架设计的,Servlet是由Java编写的服务器端程序,其主要功能在于交互式地浏览和修改数据,生成动态Web内容。一般情况下,将其理解为任何实现了这个Servlet接口的类。

        后端开发语言为Java,同时使用了Springboot框架。前端则使用HTML+CSS+JS进行编写。对于数据要存储在后端这点要求一开始我是有一些困惑的,由于没有接触过任何数据库,也有一部分时间原因,所以并没有学习怎么通过数据库存储,但是也做到了将数据存储在后端,每次要使用时再加以调用。主要方法是自己编写写入json文件、读出json文件等功能接口,将计算器计算结果以及利率等信息存入后端json文件,在需要的时候调用接口从后端读出数据或者更改数据即可。

        后端对于前端计算器传入的算式,使用了 JDK 底层调用 javaScript 的运算公式,JDK 自带的类可以实现调用 JS 的功能,可以实现执行字符串中的运算公式的功能,同时也直接避免精度丢失。

  • 代码说明

前端

style.css

table{
    margin:0 auto;
    background-color: rgb(234,236,243);
    padding: 10px 10px 10px 10px;
    color: rgb(40,49,85);
    border-radius: 20px;
    box-shadow: 1.5px 1.5px 1.5px rgba(0, 0, 0, .2);
}
.but{
    width: 80px;
    height: 60px;
    background-color : white;
    font-size: 25px;
    margin: 10px 10px 0 0;
    border-radius: 20px;
    background-color: rgb(250,250,250);
    box-shadow: 1.5px 1.5px 1.5px rgba(0, 0, 0, .2);
    border:none;
    transition: all 0.15s;
}
.but:hover{
    box-shadow: 4px 4px 4px rgba(0, 0, 0, .2);
}

.but_row, .but_column{
    background-color: rgb(221,224,235);
}
.but_ac{
    background-color: rgb(255,181,47);
    color: rgb(248,248,249);
}
.but_back{
    background-color: rgb(255,181,47);
    color: rgb(248,248,249);
}
.but_eq{
    background-color: rgb(79,110,242);
    color: rgb(248,248,249);
}
.but_reverse{
    background-color: rgba(10, 41, 236, 0.24);
    color: rgb(248,248,249);
}
.screen{
    width: 520px;
    height: 120px;
    font-size: 35px;
    margin:0 auto;
    padding: 0 15px 0 15px;
    background-color: white;
    text-align:right;       /* 使用户输入的表达数从屏幕的右边开始显示 */
    border-radius: 10px;
    font-family: "Agency FB";
}

这段代码定义了前端计算器的界面风格以及按钮样式,其中利用阴影以及按钮去尖角使页面更加美观。

script.js

$(document).ready(function () {

    let expression = "";

    /*点击输入值*/
    $(".but_val").click(function () {
        let type = $(this).val();
        if (expression.trim() === "ERROR".trim()) {
            expression = type;
        } else {
            expression = expression + type;
        }
        $(".screen").val(expression);
    })

    /*点击清除*/
    $(".but_back").click(function () {
        expression = expression.slice(0, expression.length - 1);
        $(".screen").val(expression);
    })

    /*点击清空*/
    $(".but_ac").click(function () {
        expression = "";
        $(".screen").val(expression);
    })

    /*点击等号提交*/
    $(".but_submit").click(function () {
        if (expression == null || expression === '' || expression === 'ERROR' ||
            !expression.includes('+') && !expression.includes('-') && !expression.includes('×') && !expression.includes('÷')
            && !expression.includes('sin') && !expression.includes('cos') && !expression.includes('tan')
            && !expression.includes('√') && !expression.includes('ln') && !expression.includes('%')
            && !expression.includes('pow')){
            $(".screen").val(expression);
        } else {
            expression = $(".screen").val();
            $.post("./calculate", {//!!!!!!!!注意./
                "expression": expression
            }, function (result) {
                expression = result;
                $(".screen").val(expression);
            });
        }
    })
});

这段代码 定义了点击不同按钮时触发的事件,用来描述网页的行为,其中当按下=键会进行一个初步判断,判断是否需要进行计算。同时在这里用到了 Ajax 函数 post,post() 方法通过 HTTP POST 请求从服务器载入数据。其语法如下:

jQuery.post(url,data,success(data, textStatus, jqXHR),dataType)

 HTML文件

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>计算器</title>
    <link rel="stylesheet" type="text/css" href="./css/style.css">
    <script type="text/javascript" src="./js/jquery-3.6.1.min.js"></script>
    <script type="text/javascript" src="./js/script.js"></script>
</head>
<body>
    <body>
        <table>
             <tr>
                 <td colspan="6"><label>
                     <input class="screen" type="text" disabled/>
                 </label></td>
             </tr>
             <tr>
                 <td><input class="but but_val montage but_row" type="button" value="("></td>
                 <td><input class="but but_val but_row" type="button" value=")"></td>
                 <td><input class="but but_val but_row" type="button" value="%"></td>
                 <td><input class="but_back but" type="button" value="DEL"></td>
                 <td><input class="but_ac but" type="button" value="AC"></td>
                 <td><input class="but but_val" type="button" value=","></td>
             </tr>
             <tr>
                 <td><input class="but but_val" type="button" value="7"></td>
                 <td><input class="but but_val" type="button" value="8"></td>
                 <td><input class="but but_val" type="button" value="9"></td>
                 <td><input class="but but_val but_column" type="button" value="÷"></td>
                 <td><input class="but but_val but_column" type="button" value="sin"></td>
                 <td><input class="but but_val but_column" type="button" value="pow"></td>
             </tr>
             <tr>
                 <td><input class="but but_val" type="button" value="4"></td>
                 <td><input class="but but_val" type="button" value="5"></td>
                 <td><input class="but but_val" type="button" value="6"></td>
                 <td><input class="but but_val but_column" type="button" value="×"></td>
                 <td><input class="but but_val but_column" type="button" value="cos"></td>
                 <td><input class="but but_val but_column" type="button" value="ln"></td>
             </tr>
             <tr>
                 <td><input class="but but_val" type="button" value="1"></td>
                 <td><input class="but but_val" type="button" value="2"></td>
                 <td><input class="but but_val" type="button" value="3"></td>
                 <td><input class="but but_val but_column" type="button" value="-"></td>
                 <td><input class="but but_val but_column" type="button" value="tan"></td>
                 <td><input class="but but_val but_column" type="button" value="√"></td>
             </tr>
             <tr>
                 <td><input class="but but_val" type="button" value="0"></td>
                 <td><input class="but but_val" type="button" value="."></td>
                 <td><input class="but but_submit but_eq" type="button" value="="></td>
                 <td><input class="but but_val but_column" type="button" value="+"></td>
                 <td><input class="but but_val" type="button" value="π"></td>
                 <td><input class="but but_val" type="button" value="e"></td>
             </tr>
       </table>

       </body>
       <p style="margin: 20px 20px"> <a style="font-size: 30px;font-weight:bold;color:orange " href="./list.html">查询历史结果→</a></p>
    <p style="margin: 20px 20px"> <a style="font-size: 30px;font-weight:bold;color:orange " href="./savemoney.html">利息计算器→</a></p>
</body>
</html>
<!DOCTYPE html>
<head>
    <meta charset="utf-8" />
    <title>个人存款计算器</title>
    <script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script>
    <script src="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
    <link href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<div class="text-center center-block">
<table class="table table-bordered">
    <tr class="bg-primary text-center">
        <td colspan="2">存款利息计算器</td>
    </tr>
    <tr>
        <td>存款金额</td>
        <td>
            <input placeholder="请输入数据" class="form-control" type="text" id="money">
        </td>
    </tr>
    <tr>
        <td>存款方式</td>
        <td style ="text-align:left;">
            <input id="活期" type="radio" checked="checked" name="method"/>活期
            <input type="radio" id="定期" name="method"/>定期
        </td>
    </tr>
    <tr>
        <td>存款期限</td>
        <td>
            <select class="form-control" id="year">
                <option value="0.0285">三个月</option>
                <option value="0.0305">半年</option>
                <option value="0.0325">一年</option>
                <option value="0.0415">二年</option>
                <option value="0.0475">三年</option>
                <option value="0.0525">五年</option>
            </select>
        </td>
    </tr>
    <tr>
        <td>所得利息</td>
        <td>
            <input readonly="readonly"  class="form-control" type="text" id="balance">
        </td>
    </tr>
    <tr>
        <td>本息合计</td>
        <td>
            <input class="form-control" readonly="readonly" type="text" id="total">
        </td>
    </tr>
    <tr>
        <td colspan="2">
            <button onclick="calc()" class="btn btn-danger btn-block">开始计算</button>
        </td>
    </tr>
</table>
<p style="margin: 20px 20px"> <a style="font-size: 30px;font-weight:bold;color:orange " href="./index.html">←科学计算器</a></p>
<p style="margin: 20px 20px"> <a style="font-size: 30px;font-weight:bold;color:orange " href="./borrowmoney.html">贷款利息计算器→</a></p>
</div>

 这两段代码主要定义了计算器页面布局的信息,包括相关页面的跳转。

后端

(由于代码过于冗长,这里以科学计算器为例)

data.json

data.json文件用于将前端输入的计算结果记录到后端本地文件中,在每次查询历史记录时进行后端文件查询,再将查询到的数据返回到前端页面。该文件中信息包含计算结果、输入表达式以及请求计算时间。

public class CalculatorServiceImpl implements CalculatorService {
    private CalculatorDao calculatorDao;
    @Override
    public String calculator(String expression) {
        //前端传入的表达式需要替换
        expression = expression
                .replaceAll("×", "*")
                .replaceAll("÷", "/")
                .replaceAll("π","Math.PI")
                .replaceAll("sin","Math.sin")
                .replaceAll("cos","Math.cos")
                .replaceAll("tan","Math.tan")
                .replaceAll("√","Math.sqrt")
                .replaceAll("e","Math.E")
                .replaceAll("ln","Math.log")//JAVA里log表示ln
                .replaceAll("pow","Math.pow");
        String result = "";
        //这里捕获异常 如果表达式出错
        try {
            //这是将计算表达式 传给CalculateUtil 计算出表达式
            result = CalculateUtil.execute(expression);
        } catch (ScriptException e) {
            result = "ERROR";

        }
        //将结果写入data.json 文件中
        calculatorDao.saveRecord(expression, result, DateUtil.getNowTime());
        return result;
    }
    /*
     * 这里是查询历史结果
     */
    @Override
    public String getRecord() {
        return calculatorDao.readRecord();
    }
}

这段代码对传入的算式进行初步判断,替换掉一些字符,方便后续计算。同时将结果写入后端data.json文件中 ,以便于查询历史记录。

@WebServlet("/calculate")
public class CalculatorServlet extends HttpServlet {

    private CalculatorService calculatorService;

    @Override
    public void init() {
        WebApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
        calculatorService = webApplicationContext.getBean(CalculatorService.class);
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("UTF-8");
        resp.setCharacterEncoding("UTF-8");

        String expression = req.getParameter("expression");
        //计算结果
        String result = calculatorService.calculator(expression);

        PrintWriter out = resp.getWriter();
        out.println(result);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}

 这段代码是对计算机接口的描述与定义, doGet() 方法有两个参数,第一个参数是 HttpServletRequest 类的对象,第二个参数是 HttpServletResponse 类的对象。我们这里使用它来获取计算结果。

public class CalculateUtil {

    public static String execute(String str) throws ScriptException {
        ScriptEngine jse = new ScriptEngineManager().getEngineByName("JavaScript");
        return jse.eval(str).toString();//用toString()
    }
}

这段代码是计算器计算的核心, 使用了 JDK 底层调用 javaScript 的运算公式,JDK 自带的类可以实现调用 JS 的功能,可以实现执行字符串中的运算公式的功能,同时也直接避免精度丢失。

@Repository
public class CalculatorDaoImpl implements CalculatorDao {

    /**写入的文件绝对路径*/
    private static String jsonPath = "E:\\ideaproject\\java-web-calculator-master\\src\\main\\resources\\data.json";
    /**是否追加或覆盖*/
    private static Boolean isAppend = true;
    @Override
    public String readRecord() {
        /** 用到JsonUtil 工具类 */
        return JsonUtil.readJson(jsonPath);
    }
    @Override
    public void saveRecord(String expression, String result, String submitTime) {

        Map<String, String> map = new HashMap<>();
        map.put("expression", expression);
        map.put("result", result);
        map.put("submitTime", submitTime);
        /** 利用JsonUtil 将 表达式 结果  提交的时间 以json 格式写入 文件中 **/
        Boolean isSuccess = JsonUtil.writeJson(jsonPath, map, isAppend);

        if (!isSuccess) {
            System.out.println("保存失败!");
        }
    }
}

这一部分定义了怎么写入json文件以及写入的内容,包括文件的绝对路径和是否覆盖等信息。

public class JsonUtil {

    /**
     * 往json文件中写入数据
     *
     * @param jsonPath json文件路径
     * @param inMap    Map类型数据
     * @param flag     写入状态,true表示在文件中追加数据,false表示覆盖文件数据
     * @return 写入文件状态  成功或失败
     */
    public static Boolean writeJson(String jsonPath, Map<String, String> inMap, boolean flag) {
        // Map数据转化为Json,再转换为String
        String data = new JSONObject(inMap).toString();
        File jsonFile = new File(jsonPath);
        try {
            // 文件不存在就创建文件
            if (!jsonFile.exists()) {
                jsonFile.createNewFile();
            }
            FileWriter fileWriter = new FileWriter(jsonFile.getAbsoluteFile(), flag);
            BufferedWriter bw = new BufferedWriter(fileWriter);
            bw.write(data + ",");
            bw.close();
            return true;
        } catch (IOException e) {
            return false;
        }
    }

    /**
     * 读取json文件数据
     *
     * @param jsonPath json文件路径
     * @return 字符串
     */
    public static String readJson(String jsonPath) {
        File jsonFile = new File(jsonPath);
        String result = "";
        try {
            FileReader fileReader = new FileReader(jsonFile);
            BufferedReader reader = new BufferedReader(fileReader);
            StringBuilder sb = new StringBuilder();
            while (true) {
                int ch = reader.read();
                if (ch != -1) {
                    sb.append((char) ch);
                } else {
                    break;
                }
            }
            fileReader.close();
            reader.close();
            result = sb.toString();
            return "[" + result.substring(0, result.length()-1) + "]";
        } catch (IOException e) {
            return "[]";
        }
    }

}

 这部分是对json文件进行操作的主要接口,写接口使用Map将数据转化为json写入文件。都利用了FileWriter和FileReader的Web API接口,极大提高了存取速度和效率。它们允许 Web 应用程序异步写入或读取存储在用户计算机上的文件(或原始数据缓冲区)的内容。

  • 心路历程和收获

        本次作业其实没有做到最好,但是不完美也是一种对自己的激励吧。虽然这次没用数据库也实现了后端数据存储,但是之后希望自己可以去深入学习一下MYSQL,拓展一下自己的技术。这次也有点想挑战一下自己,尝试接触了自己不熟悉的Java。学习的过程就花了很长时间,还有就是对于环境的配置,Tomcat等等,都让我体会到开发真的不是一件容易的事。往往那种找不出语法错误却又跑不通的bug是最耗时间的,这次就碰到了修改了HTML页面但是运行后没有改变的情况,但是好在看开了一点,多刷新几次,问题就解决了。这次的作业是一次挺好的经历,希望未来自己能好好珍惜现有的学习时间。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值