Gin,Gorm实现Web计算器

目录

        • 仓库链接
        • 0.PSP表格
        • 1. 成品展示
          • 1.基础运算
          • 2. 清零回退
          • 3.错误提示
          • 4.历史记录
          • 拓展功能
            • 1.前端可修改的利率计算器
            • 2.科学计算器
            • 3. 按钮切换不同计算器模式
            • 4.用户在一次运算后不清零继续输入操作符,替换表达式为上次答案
        • 2.设计实现过程
        • 3.代码说明
        • 4.心路历程和收获

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

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

0.PSP表格
PSP Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 40 20
• Estimate 估计这个任务需要多少时间 10 10
Development 开发 650 630
• Analysis 需求分析 (包括学习新技术) 40 30
• Design Spec 生成设计文档 80 75
• Design Review 设计复审 35 45
• Coding Standard 代码规范 (为目前的开发制定合适的规范) 25 20
• Design 具体设计 35 30
• Coding 具体编码 325 320
• Code Review 代码复审 65 60
• Test 测试(自我测试,修改代码,提交修改) 60 50
Reporting 报告 95 90
• Test Report 测试报告 25 20
• Size Measurement 计算工作量 10 15
• Postmortem & Process Improvement Plan 事后总结, 并提出过程改进计划 45 55
合计 670 630
1. 成品展示
1.基础运算

Gin,Gorm实现Web计算器_第1张图片

2. 清零回退

Gin,Gorm实现Web计算器_第2张图片

3.错误提示

Gin,Gorm实现Web计算器_第3张图片

4.历史记录

Gin,Gorm实现Web计算器_第4张图片

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

Gin,Gorm实现Web计算器_第5张图片

2.科学计算器

Gin,Gorm实现Web计算器_第6张图片

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

Gin,Gorm实现Web计算器_第7张图片

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

Gin,Gorm实现Web计算器_第8张图片

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) => `
          
${index}"> $ ${item.Exp} = ${item.Val} $
`
).join('
'
); 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代码不断增删猜测效果/询问他人。感觉前端代码实在写的不堪入目,还好最后效果感觉还行。
有些疑惑为什么要一个人完成前后端,可能全栈是学院派的宿命。

你可能感兴趣的:(gin,前端)