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\n",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\n", 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\n",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\n",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\n",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\n",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\n",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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM