Gin,Gorm实现Web计算器

仓库链接
2301-计算机学院-软件工程https://bbs.csdn.net/forums/ssynkqtd-05
这个作业要求在哪里https://bbs.csdn.net/topics/617377308
这个作业的目标实现一个前后端分离计算器
其他参考文献

backend
frontend
Google 开源项目风格指南——中文版
Google Style Guides

0.PSP表格
PSPPersonal Software Process Stages预估耗时(分钟)实际耗时(分钟)
Planning计划4020
• Estimate估计这个任务需要多少时间1010
Development开发650630
• Analysis需求分析 (包括学习新技术)4030
• Design Spec生成设计文档8075
• Design Review设计复审3545
• Coding Standard代码规范 (为目前的开发制定合适的规范)2520
• Design具体设计3530
• Coding具体编码325320
• Code Review代码复审6560
• Test测试(自我测试,修改代码,提交修改)6050
Reporting报告9590
• Test Report测试报告2520
• Size Measurement计算工作量1015
• Postmortem & Process Improvement Plan事后总结, 并提出过程改进计划4555
合计670630
1. 成品展示
1.基础运算

在这里插入图片描述

2. 清零回退

在这里插入图片描述

3.错误提示

在这里插入图片描述

4.历史记录

在这里插入图片描述

拓展功能
1.前端可修改的利率计算器

在这里插入图片描述

2.科学计算器

在这里插入图片描述

3. 按钮切换不同计算器模式

在这里插入图片描述

4.用户在一次运算后不清零继续输入操作符,替换表达式为上次答案

在这里插入图片描述

2.设计实现过程
  1. 前端:前端需要在上一轮作业的基础上加以完善。首先要扩充为两个输入框,一个用于显示用户输入的表达式,一个用于显示答案。同时在上一轮的form基础上再做一个利率计算器(利率计算器和存款计算器可以共用一个div)。同时使用fetch向后端传发数据。
  2. 后端:后端使用Gin做Web框架。通过路由组和hander接前端请求。Gin相比于Hertz是一个轻量级的框架适合做这种小项目。数据库交互用Gorm。
  3. 数据库:只用到了Mysql。存历史记录,存款、贷款三张表。暂且把这个计算器看作单机版本,所以在存历史记录的时候没有特意区分不同用户ID。可视化工具推荐Dbeaver。
3.代码说明
  1. 前端采用fetch通信。fetch的语法很简明,不再赘述。以查询历史记录为例:
function ans() {
    fetch("http://localhost:8080/history/", {
        method: 'GET',
        headers: {
            'Content-Type': 'application/json'
        },
    })
        .then(response => response.json())
        .then(data => {
            // 处理后端的响应
            if (data.code === 200) {
                const expVal = data.result;

                if (expVal.length > 0) {
                    const displayText = expVal.map((item, index) => `
          <div class="expression-container" id="expression-${index}">
            $ ${item.Exp} = ${item.Val} $
          </div>
        `).join('<br>');

                    Swal.fire({
                        title: '历史记录(近 10 条)',
                        html: displayText,
                        icon: 'success',
                        confirmButtonText: '关闭'
                    });

                    // 使用MathJax渲染数学表达式
                    for (let i = 0; i < expVal.length; i++) {
                        MathJax.Hub.Queue(["Typeset", MathJax.Hub, `expression-${i}`]);
                    }

                } else {
                    // 如果没有之前的数据,显示提示消息
                    Swal.fire('没有可显示的数据', '', 'error');
                }
            } else {
                // code 不为 200,给出提示
                console.error(`响应状态不是 200,错误消息:${data.msg}`);
            }
        })
        .catch(error => {
            console.error("An error occurred while receiving the result from the backend: " + error);
        });
}

页面的切换用一个变量来控制,相当于一个mod 3 的加法,控制div的可见性。

function mode() {
    st = (st + 1) % 3;
    const normalCalculator = document.querySelector('.normal');
    const interestCalculator = document.querySelector('.interest');
    let bt = document.getElementById("topMode");

    var updateInter = document.getElementById("update_inter");
    var updateInter2 = document.getElementById("update_inter2");
    if (st === 0) {
        normalCalculator.style.display = 'block';
        interestCalculator.style.display = 'none';
        bt.innerHTML = "科学计算器"
    } else if (st === 1) {
        normalCalculator.style.display = 'none';
        interestCalculator.style.display = 'block';
        updateInter.style.display = "block";
        updateInter2.style.display = "none";
        bt.innerHTML = "存款计算器"
    } else if (st === 2) {
        normalCalculator.style.display = 'none';
        interestCalculator.style.display = 'block';
        updateInter2.style.display = "block";
        updateInter.style.display = "none";
        bt.innerHTML = "贷款计算器"
    }
}
  1. 后端启动服务:
package main

import (
	"WebCalculator/dal/mysql"
	"WebCalculator/router"
	"github.com/gin-contrib/cors"
	"github.com/gin-gonic/gin"
)

func main() {
	mysql.Init()
	r := gin.Default()
	// 解决跨域问题
	r.Use(cors.Default())
	router.SetUpRoutes(r)
	err := r.Run(":8080")
	if err != nil {
		panic(err)
	}
}

注意需要解决跨域问题。查阅资料得知最近的Gin官方已经给出了对于跨域的问题的默认配置无需再手动配置,一行代码就可以了。同时在main函数中初始化mysql相关链接信息和路由组。

func SetUpRoutes(engine *gin.Engine) {
	home := engine.Group("/")
	{
		home.GET("/hello", hello.Hello)
	}
	his := engine.Group("/history")
	{
		his.POST("/", history.AddHistory)
		his.GET("/", history.QueryHistory)
	}
	dep := engine.Group("/deposit")
	{
		dep.POST("/", deposit.UpdateDep)
		dep.GET("/", deposit.QueryInterest)
	}
	l := engine.Group("/loans")
	{
		l.GET("/", loans.QueryInterest)
		l.POST("/", loans.UpdateLoans)
	}
}

hander包编写具体实现方法。
以查询利息为例:

func QueryInterest(c *gin.Context) {
	// 获取查询参数
	principalStr := c.DefaultQuery("principal", "")
	durationStr := c.DefaultQuery("duration", "")

	// 将字符串转换为浮点数
	principal, err1 := strconv.ParseFloat(principalStr, 64)
	duration, err2 := strconv.ParseFloat(durationStr, 64)

	if err1 != nil || err2 != nil {
		// 处理转换错误,例如返回错误响应
		c.JSON(http.StatusOK, gin.H{
			"code": 400,
			"msg":  "输入无效",
		})
		return
	}

	interest := service.CalInterest(entity.MoneyCal{
		Money:    principal,
		Duration: duration,
	}, 1)
	c.JSON(http.StatusOK, gin.H{
		"code":   200,
		"msg":    "计算成功",
		"result": interest,
	})

}

注意存款和贷款的逻辑几乎相同。所以可以增加一个传入参数op,0/1表示不同类型就可以复用计算函数。

func ExistDuration(duration float64, op int) bool {
	if op == 1 {
		result := mysql.DB.Where("duration = ?", duration).First(&entity.Deposit{})
		return result.Error != gorm.ErrRecordNotFound
	}
	result := mysql.DB.Where("duration = ?", duration).First(&entity.Loan{})
	return result.Error != gorm.ErrRecordNotFound
}

func CalInterest(cal entity.MoneyCal, op int) float64 {
	duration := cal.Duration
	money := cal.Money

	if op == 1 {
		var deposit entity.Deposit
		mysql.DB.Where("duration <= ?", duration).Order("duration desc").Limit(1).First(&deposit)
		return money * deposit.Rate / 100
	}

	var loans entity.Loan
	mysql.DB.Where("duration <= ?", duration).Order("duration desc").Limit(1).First(&loans)
	return money * loans. Rate / 100
}

3.数据库
Gorm的数据库操作十分便利。提供了AutoMigrate来自动迁移表结构。有结构体的情况下可以自动建对应表.无需编写sql。

DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
		SkipDefaultTransaction: true,
		PrepareStmt:            true,
		Logger:                 logger.Default.LogMode(logger.Info),
	})
	if err != nil {
		panic(err)
	}

	err = DB.AutoMigrate(&entity.History{}, &entity.Deposit{}, &entity.Loan{})
	if err != nil {
		return
	}

且gorm.Model包含许多实用信息,ID,CT,UPT等等。

4.心路历程和收获

后端部分其实没啥技术含量,主要是CRUD…
感觉最难的部分还是在前端,因为之前不会,所以几乎是一直在对着已有的HTML和CSS代码不断增删猜测效果/询问他人。感觉前端代码实在写的不堪入目,还好最后效果感觉还行。
有些疑惑为什么要一个人完成前后端,可能全栈是学院派的宿命。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

102101141高孙炜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值