zoukankan      html  css  js  c++  java
  • go语言书籍管理系统(基于gin)

    go语言书籍管理系统(基于gin)

    项目要求

    写一个web服务器,完成对书籍的管理。

    包括

    • 书籍列表展示
    • 书籍的增删改查

    展示的书籍的信息有

    • 书籍名称
    • 价格

    思路分析

    这是一个典型的web开发。

    总体分为两部分:前端页面后端服务器。在后端还涉及到数据库的操作

    大概的逻辑为:

    1. 用户在浏览器输入url请求访问页面内容
    2. 后端根据设置好的路由决定其调用哪个处理器(handler,就是个函数)
    3. 在处理函数中,会完成处理的逻辑,其中可能还与数据库发送交互,渲染HTML页面并发给浏览器。

    源码

    目录结构

    mark

    源码

    main.go

    package main
    
    import (
    	"fmt"
    	"github.com/gin-gonic/gin"
    	"net/http"
    	"strconv"
    )
    
    //BookManagementSystem
    func main(){
    	//程序启动连接数据库
    	err:=	initDB()
    	if err != nil {
    		panic(err)
    	}
    
    	r:=gin.Default()
    	//解析模板
    	r.LoadHTMLGlob("template/**/*")//模板解析
    	//各个路由	
    	r.GET("/book/list",booklListHandle)//查询书籍
    	r.GET("/book/new",newBookhandle)//增加书籍,第一次get返回html模板,给用户填写
    	r.POST("/book/new",createBookHandle)
    	r.GET("/book/delete",deleteHandle)//删除书籍
    	r.GET("/book/update",newHandle)//更新书籍信息,价格或书名
    	r.POST("/book/update",updateHandle)
    	r.Run()
    }
    //查询书籍信息
    func booklListHandle(c *gin.Context){
    	//选数据库
    	//查数据
    	//返回浏览器
    	bookList,err:=queryAlllBook()
    	if err != nil {
    		c.JSON(http.StatusBadRequest,gin.H{
    			"err":err.Error(),
    			"code":1,
    		})
    		return
    	}
    	//以json格式返回
    	//c.JSON(http.StatusOK,gin.H{//前后端分离的一个标准返回
    	//	"code":0,
    	//	"data":bookList,
    	//})
        
    	//以模板形式返回,因此需要再建一个book_list.html模板
    	c.HTML(http.StatusOK,"book/book_list.html",gin.H{
    			"code":0,
    			"data":bookList,
    	})
    
    }
    
    //查插入书籍数据
    func newBookhandle(c *gin.Context){
    	c.HTML(http.StatusOK,"book/new_book.html",nil)
    }
    
    //增加书籍
    func createBookHandle(c *gin.Context){
    	//增加新书,从form表单中提取数据
    	titleVal:=c.PostForm("title")
    	priceVal:=c.PostForm("price")
    	//上面接受到的时string类型,存储前还需类型转换一下
    	price,err:=strconv.ParseFloat(priceVal,64)
    	if err != nil {
    		 fmt.Println("转换失败")
    		return
    	}
    	//将提取的数据写入数据库,调用写好的insertAlllBook()
    	err=insertAlllBook(titleVal,price)
    	if err != nil {
    		c.String(http.StatusOK,"插入数据失败")
    		return
    	}
    	//到此,数据插入成功
    	//为了友好的交互,跳转到,书籍显示界面
    	//使用重定向进行跳转
    	c.Redirect(http.StatusMovedPermanently,"/book/list")
    }
    
    //删除书籍
    func deleteHandle(c *gin.Context){
    	//拿去query-string数据,然后根据不同数据删除指定编号的书籍
    	idVal:=c.Query("id")
    	//将 ID转换为整型
    	id,err:=strconv.ParseInt(idVal,10,64)
    	if err != nil {
    		c.JSON(http.StatusOK,gin.H{
    			"err":err.Error(),
    			"code":1,
    		})
    	}
      //删除数据
    	err=deleteBook(id)
    	if err != nil {
    		c.JSON(http.StatusOK,gin.H{
    			"err":err.Error(),
    			"code":1,
    		})
    	}
    	//重定向到书籍展示界面
    	c.Redirect(http.StatusMovedPermanently,"/book/list")
    }
    //显示书籍更新页面
    func newHandle(c *gin.Context) {
    	//拿去query-string数据,然后根据不同数据删除指定编号的书籍
    	idVal := c.Query("id")
    	//将 ID转换为整型
    	id, err := strconv.ParseInt(idVal, 10, 64)
    	if err != nil {
    		c.JSON(http.StatusOK, gin.H{
    			"err":  err.Error(),
    			"code": 1,
    		})
    		return
    	}
    	 book,err:= querySingalBook(id)
    	if err != nil {
    		c.JSON(http.StatusBadRequest, gin.H{
    			"err":  err.Error(),
    			"code": 1,
    		})
    		return
    	}
    	//向指定路径发送html模板
    	c.HTML(http.StatusOK, "book/updatebook.html", book)
    }
    func updateHandle(c *gin.Context){
    	//拿到更改信息(form表单内容)和query-string
    	//
    	////拿去query-string数据,然后根据不同数据删除指定编号的书籍
    	//idVal := c.Query("id")
    	//
    	//fmt.Printf("id=%v
    ",idVal)
    	////将 ID转换为整型
    	//id, err := strconv.ParseInt(idVal, 10, 64)
    	//if err != nil {
    	//	c.JSON(http.StatusOK, gin.H{
    	//		"err":  err.Error(),
    	//		"code": 1,
    	//	})
    	//	return
    	//}
    
    	//拿到form表单里的update信息
    	//增加新书,从form表单中提取数据
    	titleVal:=c.PostForm("title")
    	priceVal:=c.PostForm("price")
    	idVal:=c.PostForm("id")
    	//上面接受到的时string类型,存储前还需类型转换一下
    	price,err:=strconv.ParseFloat(priceVal,64)
    	if err != nil {
    		fmt.Println("转换失败")
    		return
    	}
    	id,err:=strconv.ParseInt(idVal, 10, 64)
    	if err != nil {
    		fmt.Println("转换失败")
    		return
    	}
    	//在数据库中更新
    	err=updateBook(titleVal,price,id)
    	if err != nil {
    		c.JSON(http.StatusOK, gin.H{
    			"err":  err.Error(),
    			"code": 1,
    		})
    		return
    	}
    	//重定向到书籍展示界面
    	c.Redirect(http.StatusMovedPermanently,"/book/list")
    }
    

    db.go

    package main
    
    import (
    	"fmt"
    	"github.com/jmoiron/sqlx"
    	_ "github.com/go-sql-driver/mysql"
    )
    
    //跟数据库相关
    var db *sqlx.DB
    //初始化连接数据库
    func initDB()(err error){
    	dsn := "root:5210@tcp(127.0.0.1:3306)/go_test"
    	// 也可以使用MustConnect连接不成功就panic
    	db, err = sqlx.Connect("mysql", dsn)
    	if err != nil {
    		fmt.Printf("connect DB failed, err:%v
    ", err)
    		return
    	}
    	db.SetMaxOpenConns(20)//设置最大连接数
    	db.SetMaxIdleConns(10)
    	return
    }
    
    //查询所有数据
    func queryAlllBook()( bookList []*Book,err error){
    
    	sqlStr:="select id,title ,price from book"
    
    	err=db.Select(&bookList,sqlStr)
    	if err != nil {
    		fmt.Printf("查询信息失败err=%v
    ",err)
    		return
    	}
    	return
    }
    
    //查询单条书籍
    func querySingalBook(id int64)(book Book,err error){
    
    	sqlstr:="select id,title ,price from book where id=? "
    	err=db.Get(&book,sqlstr,id)
    	if err != nil {
    		fmt.Printf("查询信息失败1111err=%v
    ",err)
    		return
    	}
    	return
    }
    
    //插入数据
    func insertAlllBook(title string,price float64)( err error){
    
    	sqlStr:="insert into book(title,price) values (?,?)"
    
    	_,err=db.Exec(sqlStr,title,price)
    	if err != nil {
    		fmt.Printf("插入信息失败err=%v
    ",err)
    		return
    	}
    	return
    }
    
    
    //插删除据
    func deleteBook(id int64)( err error){
    
    	sqlStr:="delete from book where id=?"
    
    	_,err=db.Exec(sqlStr,id)
    	if err != nil {
    		fmt.Printf("删除信息失败err=%v
    ",err)
    		return
    	}
    	return
    }
    
    //更新除据
    func updateBook(title string,price float64,id int64)( err error){
    
    	sqlStr:="update book set title=?,price=? where id=?"
    
    	_,err=db.Exec(sqlStr,title,price,id)
    	if err != nil {
    		fmt.Printf("更新信息失败err=%v
    ",err)
    		return
    	}
    	return
    }
    
    

    model.go

    package main
    
    //专门定义与数据对应的结构体
    //结构体对应数据库的一张表
    type Book struct {
    	ID int64`db:"id"` //和数据库联系加一个db的tag
    	Title string`db:"title"`
    	Price float64`db:"price"`
    }
    

    book下的三张html表

    {{ define "book/book_list.html"}}  //用于展示书籍历表信息
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>书籍列表展示</title>
    </head>
    <body>
    
    
    <div> <a href="/book/new"> 添加新书 </a></div>
        <table border="1">
            <thead>
                <tr>
                    <th>ID</th>
                    <th>Title</th>
                    <th>Price</th>
                    <th>操作</th>
                </tr>
            </thead>
            <tbody>
                {{range .data}}
                    <tr>
                        <td> {{.ID}}</td>
                        <td> {{.Title}}</td>
                        <td> {{.Price}}</td>
                        <td><a href="/book/delete?id={{.ID}}"> 删除</a></td>
                        <td><a href="/book/update?id={{.ID}}"> 编辑</a></td>
                    </tr>
                {{end}}
            </tbody>
        </table>
    
    </body>
    </html>
    {{end}}
    
    
    ------------------------------------------------------------------------------------
    
    {{ define "book/new_book.html"}}  //用于给用于提交from表单数据
        <!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>添加书籍信息</title>
        </head>
        <body>
    
    
    
        <form action="/book/new" method="POST" >
    
            <div>
                <label>书名
                    <input type="text" name="title">
                </label>
            </div>
            <div>
                <label>价格
                    <input type="number" name="price">
                </label>
            </div>
            <div>
                <input type="submit" name="提交">
            </div>
    
        </form>
    
    
        </body>
        </html>
    {{end}}
    
    ------------------------------------------------------------------------------------
    
    {{ define "book/updatebook.html"}}  //用于给用户在更新书籍信息是提交form表单数据
        <!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>更新书籍信息</title>
        </head>
        <body>
    
        <form action="/book/update" method="POST" >
    
            <div>
                <label>新的书名
                    <input type="text" name="title" value="{{.Title}}" >
                </label>
            </div>
            <div>
                <label>新的价格
                    <input type="number" name="price" value="{{.Price}}">
                </label>
            </div>
            <div>
                <label>id
                    <input type="number" name="id" value="{{.ID}}" readonly/>
                </label>
            </div>
            <div>
                <input type="submit" name="提交">
            </div>
    
        </form>
        </body>
        </html>
    {{end}}
    

    table.sql

    -- ------------------------------
    -- 注释  用于设置此项目锁需要的数据库表
    -- BMS
    -- --------------------------------
    
    CREATE TABLE `book`(
        `id` bigint(20) AUTO_INCREMENT PRIMARY KEY ,
        `title` varchar (20) NOT NULL ,
        `price` double (10,2 ) NOT  NULL
    )ENGINE=InnoDB DEFAULT CHARSET=utf8;
    

    重点总结

    1. 学习项目代码的分离,例如本例中与数据库的操作相关的都放在一起,项目用到的模板都放在了template目录下,专门设置一个文件存放公用的数据结构定义。

    2. 学习goweb开发的流程,以及前后端的交互方式。

    3. html模板在各个handler中渲染前要先进行模板的解析。

    4. 经常出现一种情况是对一个页面有访问,但是请求类型不同,如有时是GET(请求页面时)有时是POST(提交表单时),这是便要对不同的请求分别设置相应的handler,本例中时通过设置了两次路由实现,也可以用r.Any()函数,然后再函数里面再具体区分不同的请求类型。

    5. html模板中的第一行的define是定义模板的名字,后面再再HTML模板渲染的时候要与之一致。

    6. 本例中涉及到了两种网页的重定向:
      一种是在html中,例如<div> <a href="/book/new"> 添加新书 </a></div>,这一个是在浏览器点击添加新书按钮后跳转到/book/new界面;
      另一种实在handler函数中,逻辑处理完成后,调用c.Redirect(http.StatusMovedPermanently,"/book/list")实现页面的重定向(跳转)。

    7. 在删除书籍时涉及到一个问题就是”在点击删除按钮后,后端如何知道删除的是哪一本书籍?",这就需要在点击删除时,不同的书籍带一个唯一的标识信息,后端根据表示信息进行删除操作。问题又来了,如何将这个信息交给后端呢?我们知道前后端交互的方式大概有三种:1. query-string方式 2. form表单形式 3. 路径参数
      这里常用的是query-string方式,这就需要在HTML模板中做一些小操作,如下图

      mark

      点击删除后会跳转到 /book/delete页面,后面还跟了一个query-string,这是后端就可以解析query-string参数,得到这个唯一标识。

    8. 本题目最难的是书籍信息的修改部分。他要求实现一个小功能:在修改书籍时,输入框内要预先展示书籍的默认信息。这部分也可以分为两块,GET请求时给浏览器一个界面,让用户进行修改;POST请求时接受用户提交的表单,并在数据库中更新数据。
      在第一部分采用和删除时相同的方法,用query-string方式告诉后端要修改书籍,然后后端服务器五数据库取出这本书的原始信息,渲染给模板,发给用户作为默认信息。

      在第二部分form表单提交时同样需要告诉告诉后端是更新拿一本书。这时标识的传输可以是query-string方式,也可以用form表单,本文采用的是form表单方式。具体如下:

      mark

      这里强行在form表单里加入了该书籍的唯一标识(ID)。

      如果用query-string方式就需要修改form表单里的“action”内容,这个action是指form表单提交到什么地方。因此可以将其设置为/book/update?id={{.ID}},这样在后端就可以进行参数解析。

    调试

    书籍展示

    mark

    删除操作

    mark

    在删除完后会重定向到书籍展示界面。

    添加书籍

    mark

    mark

    编辑书籍

    mark

    mark

  • 相关阅读:
    Jenkins构建常见问题
    Jenkins搭建.NET自动编译发布远程环境
    Jenkins搭建.NET自动编译发布本地环境
    Web API系列(四) 使用ActionFilterAttribute 记录 WebApi Action 请求和返回结果记录
    C# 异步编程(async&await)
    [root@localhost ~]# /etc/httpd/conf/httpd.conf bash: /etc/httpd/conf/httpd.conf: 权限不够
    解决apache启动失败:Job for httpd.service failed.
    Windows环境下PHP配置详解
    如何以管理员身份运行cmd
    win8如何设置以管理员身份运行
  • 原文地址:https://www.cnblogs.com/wind-zhou/p/13044400.html
Copyright © 2011-2022 走看看