GO语言开发天天生鲜项目第四天 商品后台管理
商品模塊
一:商品后臺管理
我們添加商品的后臺和我們前面做的案例后臺管理很類似,這里我們就直接拿我們前面的案例來改一下,直接當我們商品后臺管理
1.代碼遷移
-
先把我們案例文件夾下的views文件夾刪除。
-
把課堂資料中的views.zip解壓到我們的classOne下面
-
在GoLand中打開項目
-
把項目中model.go文件里面的代碼改為我們天天生鮮的model.go內容(修改數據庫)
-
修改user.go和article.go中報紅的地方
-
控制頁面顯示的函數留下修改,處理業務的函數都注釋掉(因為需要處理的業務可能會發生變化,所以先注釋掉)
-
需要做的修改就是把原來表的名字和字段修改為新的表的名字和字段
-
2.登陸和注冊
注冊業務沒有什么改變,需要注意的是一些字段改變的不一樣啦,還有就是我們在后臺注冊和在天天生鮮界面注冊的權限不一致。
登陸的時候判斷也基本不變,需要注意的是要多添加一塊權限驗證:
//5.判斷是否權限登陸后臺管理,如果不等于1說明是普通用戶,不能登陸后臺if user.Power != 1{beego.Info("沒有權限登陸后臺")this.TplName = "login.html"return}登陸成功之后跳轉到列表頁:
進入到后臺之后我們先來實現增加類型業務。
3.添加類型
添加類型界面分兩塊,一塊是類型信息的展示,一塊是類型的添加
3.1.添加類型
3.1.1獲取數據
我們獲取的時候發現,添加類型需要上傳兩張圖片,上傳圖片功能,我們在添加文章函數中用過。但是如果每有一處上傳圖片的業務就得寫一次這個代碼,代碼就重復了,針對重復代碼,我們可以抽離出來一個上傳圖片的函數
3.1.2抽離上傳圖片函數
把添加文章中關于上傳文件的代碼抽離出來,先不指定參數和返回值:
//抽象上傳圖片函數 func UploadImage(){//1.那數據//那標題f,h,err:=this.GetFile(filePath)defer f.Close()//上傳文件處理//1.判斷文件格式ext := path.Ext(h.Filename)if ext != ".jpg" && ext != ".png"&&ext != ".jpeg"{beego.Info("上傳文件格式不正確")return }//2.文件大小if h.Size>5000000{beego.Info("文件太大,不允許上傳")return }//3.不能重名fileName := time.Now().Format("2006-01-02 15:04:05")err2:=this.SaveToFile(filePath,"./static/img/"+fileName+ext)if err != nil{beego.Info("上傳文件失敗")return }if err2 != nil{beego.Info("上傳文件失敗",err2)return } }然后發現,函數功能里面需要兩個參數,一個是控制器本身,因為很多beego的函數需要有beego的控制器才能調用。另外一個是上傳文件時,需要提供的name屬性。因為不同的文件上傳對應的name值也可能不一樣。多以我們給這個函數添加兩個參數。上傳完文件之后,我們還需要獲取到文件的 存儲路徑,所以需要函數返回一個路徑,這里我們給函數加一個string類型的返回值。函數完整代碼如下:
func UploadImage(this*beego.Controller,filePath string)string{//1.那數據//那標題f,h,err:=this.GetFile(filePath)defer f.Close()//上傳文件處理//1.判斷文件格式ext := path.Ext(h.Filename)if ext != ".jpg" && ext != ".png"&&ext != ".jpeg"{beego.Info("上傳文件格式不正確")return ""}//2.文件大小if h.Size>5000000{beego.Info("文件太大,不允許上傳")return ""}//3.不能重名fileName := time.Now().Format("2006-01-02 15:04:05")err2:=this.SaveToFile(filePath,"./static/img/"+fileName+ext)if err != nil{beego.Info("上傳文件失敗")return ""}if err2 != nil{beego.Info("上傳文件失敗",err2)return ""}return "/static/img/"+fileName+ext }有了上傳圖片函數之后,添加類型獲取數據代碼如下:
//1.獲取數據typename:=this.GetString("typeName")logoImage := UploadImage(&this.Controller,"uploadlogo")typeImage :=UploadImage(&this.Controller,"uploadTypeImage")3.1.2.數據校驗
對獲取的數據判斷一下,看是否為空:
//判斷數據if typename == ""||logoImage == "" || typeImage == ""{beego.Info("添加類型數據為空")return}#####3.1.3.插入數據庫
數據沒問題的話,就獲取一個GoodsType對象并給它賦值,然后插入數據庫:
//執行插入操作o := orm.NewOrm()var goodsType models.GoodsTypegoodsType.Name = typenamegoodsType.Image = typeImagegoodsType.Logo = logoImage_,err:=o.Insert(&goodsType)if err != nil{beego.Info("插入失敗")return}3.1.4.返回視圖
業務執行完之后還是返回到本頁面,看一下是否添加成功
this.Redirect("/Article/AddArticleType",302)3.2.利用fastDFS存儲圖片
3.2.1什么是FastDFS
FastDFS 是用 c 語言編寫的一款開源的分布式文件系統。FastDFS 為互聯網量身定制, 充分考慮了冗余備份、負載均衡、線性擴容等機制,并注重高可用、高性能等指標,使用 FastDFS 很容易搭建一套高性能的文件服務器集群提供文件上傳、下載等服務。
優點:
FastDFS 架構包括 Tracker server 和 Storage server。客戶端請求 Tracker server 進行文 件上傳、下載,通過 Tracker server 調度最終由 Storage server 完成文件上傳和下載。
Tracker server 作用是負載均衡和調度,通過 Tracker server 在文件上傳時可以根據一些 方法找到 Storage server 提供文件上傳服務。可以將 tracker 稱為追蹤服務器或調度服務 器。
Storage server 作用是文件存儲,客戶端上傳的文件最終存儲在 Storage 服務器上, Storageserver 沒有實現自己的文件系統而是利用操作系統 的文件系統來管理文件。可以將 storage 稱為存儲服務器。
服務端兩個角色:
Tracker:管理集群,tracker 也可以實現集群。每個 tracker 節點地位平等。收集 Storage 集群的狀態。
Storage:實際保存文件
Storage 分為多個組,每個組之間保存的文件是不同的。每 個組內部可以有多個成員,組成員內部保存的內容是一樣的,組成員的地位是一致的,沒有 主從的概念。
3.2.2文件上傳流程
客戶端上傳文件后存儲服務器將文件 ID 返回給客戶端,此文件 ID 用于以后訪問該文 件的索引信息。文件索引信息包括:組名,虛擬磁盤路徑,數據兩級目錄,文件名。
組名: 文件上傳后所在的 storage 組名稱,在文件上傳成功后有 storage 服務器返回, 需要客戶端自行保存。
虛擬磁盤路徑: storage 配置的虛擬路徑,與磁盤選項 store_path*對應。如果配置了 store_path0 則是 M00,如果配置了 store_path1 則是 M01,以此類推。
**數據兩級目錄 **:storage 服務器在每個虛擬磁盤路徑下創建的兩級目錄,用于存儲數據 文件。
文件名 :與文件上傳時不同。是由存儲服務器根據特定信息生成,文件名包含:源存儲 服務器 IP 地址、文件創建時間戳、文件大小、隨機數和文件拓展名等信息。
3.2.3文件下載流程
3.2.4簡易FastDFS架構
3.2.5FastDFS安裝
3.2.5.1安裝FastDFS依賴包
3.2.5.2安裝FastDFS
3.2.5.3配置跟蹤服務器tracker
在/home/itcast/目錄中創建目錄 fastdfs/tracker
mkdir –p /home/itcast/fastdfs/tracker編輯/etc/fdfs/tracker.conf配置文件 sudo vim /etc/fdfs/tracker.conf
? 修改 base_path=/home/itcast/fastdfs/tracker
3.2.5.4配置存儲服務器storage
在/home/itcast/fastdfs/ 目錄中創建目錄 storage
mkdir –p /itcast/python/fastdfs/storage編輯/etc/fdfs/storage.conf配置文件 sudo vim /etc/fdfs/storage.conf
修改內容:
base_path=/home/itcast/fastdfs/storage store_path0=/home/itcast/fastdfs/storage tracker_server=自己ubuntu虛擬機的ip地址:221223.2.5.5啟動tracker和storage
進入到/etc/fdfs/下面執行以下兩條指令
sudo fdfs_trackerd /etc/fdfs/tracker.conf sudo fdfs_storaged /etc/fdfs/storage.conf3.2.5.6測試是否安裝成功
修改內容:
base_path=/home/python/fastdfs/tracker tracker_server=自己ubuntu虛擬機的ip地址:22122上傳文件測試(fastDHT)
sudo fdfs_upload_file /etc/fdfs/client.conf 要上傳的圖片文件
如果返回類似**group1/M00/00/00/rBIK6VcaP0aARXXvAAHrUgHEviQ394.jpg **的文件id則說明文件上傳成功
3.2.5.7安裝fastdfs-nginx-module
解壓縮 nginx-1.8.1.tar.gz
解壓縮 fastdfs-nginx-module-master.zip
進入nginx-1.8.1目錄中
執行
sudo ./configure --prefix=/usr/local/nginx/ --add-module=fastdfs-nginx-module-master解壓后的目錄的絕對路徑/src注意:這時候會報一個錯,說沒有PCRE庫
下載缺少的庫
sudo apt-get install libpcre3 libpcre3-dev-
首先你需要去更換源,因為ubuntu自帶的源沒有這個庫
-
更換下載源為阿里的源
-
先把原來的源文件備份
sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak -
編輯源文件
sudo vim /etc/apt/sources.list把原來的內容全部刪掉,粘貼一下內容:
deb http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse deb-src http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiversedeb http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse deb-src http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiversedeb http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse deb-src http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiversedeb http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse deb-src http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiversedeb http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse deb-src http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse更換完源之后執行
sudo apt-get update sudo apt-get install libpcre3 libpcre3-dev然后進入nginx-1.8.1目錄中,再次執行:
sudo ./configure --prefix=/usr/local/nginx/ --add-module=fastdfs-nginx-module-master解壓后的目錄的絕對路徑/src這時候還會報一個錯(錯誤還真多),錯誤原因是因為nginx編譯的時候把警告當錯誤處理,事實上這個警告并不影響(程序員忽略警告):
解決方法:
找到objs目錄下的Makefile
vim Makefile
刪掉里面的-Werror(如果沒有修改權限,修改一下這個文件的權限,chmod 777 Makefile)
然后回到nginx-1.8.1目錄中,再次執行:
sudo ./configure --prefix=/usr/local/nginx/ --add-module=fastdfs-nginx-module-master解壓后的目錄的絕對路徑/src執行完成后執行sudo make
執行sudo make install
sudo cp fastdfs-nginx-module-master解壓后的目錄中src下mod_fastdfs.conf /etc/fdfs/mod_fastdfs.conf
sudo vim /etc/fdfs/mod_fastdfs.conf
修改內容:
connect_timeout=10 tracker_server=自己ubuntu虛擬機的ip地址:22122 url_have_group_name=true store_path0=/home/python/fastdfs/storagesudo cp 解壓縮的fastdfs-master目錄中的conf中的http.conf /etc/fdfs/http.conf
sudo cp 解壓縮的fastdfs-master目錄中的mime.types /etc/fdfs/mime.types
sudo vim /usr/local/nginx/conf/nginx.conf
在http部分中添加配置信息如下:
server {listen 8888;server_name localhost;location ~/group[0-9]/ {ngx_fastdfs_module;}error_page 500 502 503 504 /50x.html;location = /50x.html {root html;}}啟動nginx
sudo /usr/local/nginx/sbin/nginx
3.2.6使用go客戶端上傳文件測試
-
下載包
go get github.com/weilaihui/fdfs_client這時候會報一個錯:
這是因為我們的網絡有防火墻,不能直接去google下載相應的包,所以就失敗啦
解決辦法:
-
在~/workspace/go/src目錄下面創建一個golang.org/x目錄
cd ~/workspace/go/src mkdir -p golang.org/x -
進入golang.org/x下載兩個包
cd golang.org/x git clone https://github.com/golang/crypto.git git clone https://github.com/golang/sys.git -
然后再執行最初的下載命令
go get github.com/weilaihui/fdfs_client
-
-
go操作fastDFS的方法
-
先導包,把我們下載的包導入
import "github.com/weilaihui/fdfs_client" -
導包之后,我們需要指定配置文件生成客戶端對象
client,_:=fdfs_client.NewFdfsClient("/etc/fdfs/client.conf") -
接著我們就可以通過client對象執行文件上傳,上傳有兩種方法,一種是通過文件名,一種是通過字節流
-
通過文件名上傳**UploadByFilename **,參數是文件名(必須通過文件名能找到要上傳的文件),返回值是fastDFS定義的一個結構體,包含組名和文件ID兩部分內容
fdfsresponse,err := client.UploadByFilename("flieName") -
通過字節流上傳UploadByBuffer,參數是字節數組和文件后綴,返回值和通過文件名上傳一樣。
fdfsresponse,err := client.UploadByBuffer(fileBuffer,ext)
-
-
3.3.改用fastDFS上傳文件
? 我們以前保存視圖傳遞過來的文件方法是先用GetFile()獲取文件相關信息,然后再用SaveToFile()把文件保存到 文件夾下面。以前保存圖片的代碼如下:
func UploadImage(this*beego.Controller,filePath string)string{//1.那數據//那標題f,h,err:=this.GetFile(filePath)defer f.Close()//上傳文件處理//1.判斷文件格式ext := path.Ext(h.Filename)if ext != ".jpg" && ext != ".png"&&ext != ".jpeg"{beego.Info("上傳文件格式不正確")return ""}//2.文件大小if h.Size>5000000{beego.Info("文件太大,不允許上傳")return ""}//3.不能重名fileName := time.Now().Format("2006-01-02 15:04:05")err2:=this.SaveToFile(filePath,"./static/img/"+fileName+ext)if err != nil{beego.Info("上傳文件失敗")return ""}if err2 != nil{beego.Info("上傳文件失敗",err2)return ""}return "/static/img/"+fileName+ext }我們現在要用fastDFS來存儲圖片,第一步也是先用GetFile拿到文件,但是第二步,我們用UploadByBuffer()把靜態文件存到我們fastDFS文件系統中。
//先導包 import "github.com/weilaihui/fdfs_client"//通過GetFile獲取文件信息 f,h,err := this.GetFile(filePath) defer f.Close() //然后對上傳的文件進行格式和大小判斷 //1.判斷文件格式 ext := path.Ext(h.Filename) if ext != ".jpg" && ext != ".png"&&ext != ".jpeg"{beego.Info("上傳文件格式不正確")return "" }//2.文件大小 if h.Size>5000000{beego.Info("文件太大,不允許上傳")return "" } //3.上傳文件 //先獲取一個[]byte fileBuffer := make([]byte,h.Size) //把文件數據讀入到fileBuffer中 f.Read(fileBuffer) //獲取client對象 client := fdfs_client.NewFdfsClient("/etc/fdfs/client.conf") //上傳 fdfsresponse,_:=client.UploadByBuffer(fileBuffer,ext) //返回文件ID return fdfsresponse.RemoteFileId上傳圖片方式修改完成之后我們添加類型就業務就結束了。
3.4 類型展示
類型展示和案例中的展示業務一樣,只是一些表名和字段名需要簡單修改,代碼如下:
//1.讀取類型表,顯示數據o := orm.NewOrm()var goodsTypes[]models.GoodsType//查詢_,err:=o.QueryTable("GoodsType").All(&goodsTypes)if err != nil{beego.Info("查詢類型錯誤")}this.Data["title"] = "添加類型"this.Data["goodsTypes"] = goodsTypesthis.Layout = "layout.html"this.TplName = "addType.html"到這里,我們添加類型界面就算完成了。但是我們后面還需要添加goodsSPU,添加商品,刪除商品,修改商品等功能和我們以前寫的業務基本一樣,老師就不給你們寫了,你們自己去實現一下相關業務。
有的學生覺得我們這塊和以前寫的一樣,覺得沒有意思,我們這里就直接導入數據,不再一步步的添加數據
3.5導入商品有關數據
3.5.1導入數據庫數據
? 導入數據庫數據,老師給你們的資料中,有一個文件是 dailyfresh.sql,就是我以前導入的數據,你們可以把這個sql語句導入到你們的數據庫中,直接拿來使用。
-
先進入數據庫中
mysql -uroot -p123456 -
選中項目中用到的數據庫
use dailyfresh -
導入文件(保證dailyfresh.sql文件在你當前目錄下面)
source dailyfresh.sql -
查看數據是否導入成功
select * from goods_type;
3.5.2導入圖片數據
? 導入數據庫數據之后我們還有很多圖片內容是存在fastDFS中的,也需要我們手動導入
-
刪除 ~/fdfs/storage/data/00目錄下的00文件夾
-
把課堂資料中的00.zip拷貝到我們存放圖片的路徑下面 ~/fdfs/storage/data/00
-
解壓00.zip到當前目錄
這時候我們就把數據全部導入到了我們的開發環境當中。
二.商品信息展示
前面我們導入了全部商品有關的數據,接著我們就來實現,商品展示有關的內容。
1.首頁內容的展示
首先我們來看一下首頁的頁面。
商品頁面分為如圖所示的四部分,那我們就來分別獲取這四部分的值,仍舊按照我們的四步驟來走。
請求
這里我們設定首頁的請求路徑為\
路由
然后我們去router.go中修改相應的路由適配
//顯示主頁面beego.Router("/",&controllers.GoodsController{},"get:ShowIndex")**控制器 **
這里我們指定首頁對應GoodsController控制器,獲取首頁的方法為ShowIndex,接著我們就來實現這個方法。
-
獲取類型
//查詢商品類型var goodsTypes []models.GoodsTypeo.QueryTable("GoodsType").All(&goodsTypes)this.Data["types"] = goodsTypes -
獲取輪播商品數據
//查詢輪播商品圖片var goodsBanner []models.IndexGoodsBannero.QueryTable("IndexGoodsBanner").OrderBy("Index").All(&goodsBanner)this.Data["goodsBanners"] = goodsBanner -
查詢促銷商品數據
//查詢促銷商品var promotionBanner []models.IndexPromotionBannero.QueryTable("IndexPromotionBanner").OrderBy("Index").All(&promotionBanner)this.Data["proBanner"] = promotionBanner -
查詢首頁商品
這個業務比較復雜,我們觀察頁面發現我們查詢的時候要把類型對象和相應的商品對象切片放在一起,那我們用什么容器呢?分析如下圖:
分析之后我們發現,只能用interface這種類型,還需要給不同類型的數據加一個key值,所以我們需要用map[string]interface{},這是一個 類型的內容,那所有類型的內容,我們應該給這個類型放到切片中,所以我們這里用**[]map[string]interface{}**這種類型來存儲首頁商品。
確定了存儲容器之后,我們開始查詢數據來存儲,首先是把商品類型存儲到我們的容器當中:
goods := make([]map[string]interface{},len(goodsTypes))for index,goodsType := range goodsTypes{temp := make(map[string]interface{})temp["type"] = goodsTypegoods[index] = temp}接著把商品存儲到我們的容器中,注意這里是分為圖片商品和文字商品,需要分開來存儲:
var goodsImage []models.IndexTypeGoodsBanner var goodsText []models.IndexTypeGoodsBanner for _,temp := range goods{o.QueryTable("IndexTypeGoodsBanner").RelatedSel("GoodsSKU","GoodsType").Filter("GoodsType",temp["type"]).Filter("Display_Type",1).OrderBy("Index").All(&goodsImage)o.QueryTable("IndexTypeGoodsBanner").RelatedSel("GoodsSKU","GoodsType").Filter("GoodsType",temp["type"]).Filter("Display_Type",0).OrderBy("Index").All(&goodsText)temp["goodsText"] = goodsText temp["goodsImage"] = goodsImage }然后再把數據傳遞給視圖
this.Data["goods"] = goods
**視圖 **
? 獲取完數據就需要在頁面里面顯示,這里注意,我們獲取的圖片數據是FastDFS返回的ID值,這里我們訪問的是通過nginx獲取的圖片數據,所以需要在圖片地址上需要加上相應的ip和端口號,代碼如下:
<div class="center_con clearfix"><ul class="subnav fl"><!--------循環展示類型數據---------->{{range .types}}<li><a href="" class="{{.Logo}}">{{.Name}}</a></li>{{end}}</ul><div class="slide fl"><ul class="slide_pics"><!--------循環展示輪播圖---------->{{range .goodsBanners}}<li><img src="http://192.168.110.81:8888/{{.Image}}" alt="幻燈片"></li>{{end}}</ul><div class="prev"></div><div class="next"></div><ul class="points"></ul></div><div class="adv fl"><!--------循環展示促銷商品數據---------->{{range .proBanner}}<a href="{{.Url}}"><img src="http://192.168.110.81:8888/{{.Image}}"></a>{{end}}</div></div> <!--------循環展示首頁商品----------> {{range .goods}}<div class="list_model"><div class="list_title clearfix"><h3 class="fl" id="model01">{{.type.Name}}</h3><div class="subtitle fl"><span>|</span><!--------循環展示文字商品數據---------->{{range .goodsText}}<a href="">{{.GoodsSKU.Name}}</a>{{end}}</div><a href="" class="goods_more fr" id="fruit_more">查看更多 ></a></div><div class="goods_con clearfix"><div class="goods_banner fl"><img src="http://192.168.110.81:8888/{{.type.Image}}"></div><ul class="goods_list fl"><!--------循環展示圖片商品數據---------->{{range .goodsImage}}<li><h4><a href="">{{.GoodsSKU.Name}}</a></h4><a href=""><img src="http://192.168.110.81:8888/{{.GoodsSKU.Image}}"></a><div class="prize">¥ {{.GoodsSKU.Price}}</div></li>{{end}}</ul></div></div> {{end}}2.商品詳情頁的展示
首頁展示之后,我們接著來實現商品詳情頁的展示!仍然是我們你的四步驟
請求
商品詳情頁的請求應該在首頁展示的相應頁面添加,不管是文字商品還是圖片商品都應該添加。商品詳情和我們實現案例中文章詳情一樣,都需要傳遞相應的Id,所以我們設計商品詳情的路由為
/goodsDetail?id={{.GoodsSKU.Id}}
路由
然后到router.go中添加對應的路由
beego.Router("/goodsDetail",&controllers.GoodsController{},"get:ShowDetail")控制器
接著我們來實現ShowDetail方法。仍然是我們的四步驟。
-
獲取數據
id,err:= this.GetInt("id") -
校驗數據
if err != nil{beego.Error("獲取數據不存在")this.Redirect("/",302)return } -
數據處理
首先我們看一下,我們這個頁面:
由頁面可知,我們要獲取類型數據和商品數據。代碼如下:
//獲取商品類型var goodsTypes []models.GoodsTypeo.QueryTable("GoodsType").All(&goodsTypes)this.Data["types"] = goodsTypes //獲取商品詳情var goods models.GoodsSKUo.QueryTable("GoodsSKU").RelatedSel("GoodsType","Goods").Filter("Id",id).One(&goods)this.Data["goods"] = goods
視圖
我們把獲取到的商品詳細信息添加在視圖中顯示,代碼如下:
<div class="goods_detail_con clearfix"><div class="goods_detail_pic fl"><img src="http://192.168.110.81:8888/{{.goods.Image}}"></div><div class="goods_detail_list fr"><h3>{{.goods.Name}}</h3><p>{{.goods.Desc}}</p><div class="prize_bar"><span class="show_pirze">¥<em>{{.goods.Price}}</em></span><span class="show_unit">單 位:{{.goods.Unite}}</span></div><div class="goods_num clearfix"><div class="num_name fl">數 量:</div><div class="num_add fl"><input type="text" class="num_show fl" value="1" name="goodsCount"><a href="javascript:;" class="add fr">+</a><a href="javascript:;" class="minus fr">-</a></div> </div><div class="total">總價:<em>{{.goods.Price}}</em></div><div class="operate_btn"><a href="javascript:;" class="buy_btn">立即購買</a><a href="javascript:;" skuid="{{.goods.Id}}" class="add_cart" id="add_cart">加入購物車</a></div></div> </div>歷史記錄的添加和顯示
-
獲取詳情的時候我們需要添加歷史瀏覽記錄,我們介紹項目的時候就說過,我們的歷史瀏覽記錄存儲在redis中。這里我們就需要做一個歷史瀏覽記錄的存儲。在實現相關代碼之前,我們先來分析一下,我們如何去存儲 歷史瀏覽記錄。這里有以下幾個問題。
-
1.什么時候添加歷史瀏覽記錄
在登陸的情況下查看商品詳情的時候添加
-
2.什么時候獲取歷史瀏覽記錄
在用戶中心頁獲取歷史瀏覽記錄
-
3.用什么格式來存儲用戶瀏覽記錄
這里我們要考慮一下歷史瀏覽記錄都存儲哪些內容,歷史瀏覽記錄需要存儲哪個用戶瀏覽了哪些內容,而且還有先后順序,所以要存儲用戶,商品內容,而且用戶和商品是對應存儲,并且有順序,這里我們用redis數據庫中的list來存儲。存入的key值設定為history_用戶id,value值為商品id,存儲代碼如下:
//添加歷史瀏覽記錄,需要先查詢有沒有登陸,只有登陸之后可以添加歷史瀏覽記錄userName := this.GetSession("userName")if userName != nil{//查詢用戶信息var user models.Useruser.Name = userName.(string)o.Read(&user,"Name")conn,_:=redis.Dial("tcp",":6379")//插入歷史紀錄reply,err:=conn.Do("lpush","history"+strconv.Itoa(user.Id),id)reply,_ = redis.Bool(reply,err)if reply == false{beego.Info("插入瀏覽數據錯誤")}}這時候需要注意,當用戶多次瀏覽某個商品的時候,我們添加歷史瀏覽記錄只添加一條,所以在添加歷史瀏覽記錄之前需要把原來的記錄給清空。修改之后的代碼如下:
//添加歷史瀏覽記錄,需要先查詢有沒有登陸,只有登陸之后可以添加歷史瀏覽記錄userName := this.GetSession("userName")if userName != nil{//查詢用戶信息var user models.Useruser.Name = userName.(string)o.Read(&user,"Name")conn,_:=redis.Dial("tcp",":6379")//先清空以前的記錄1reply,err:=conn.Do("lrem","history"+strconv.Itoa(user.Id),0,id)reply,_ = redis.Bool(reply,err)if reply == false{beego.Info("插入瀏覽數據錯誤")}//插入歷史紀錄conn.Do("lpush","history"+strconv.Itoa(user.Id),id)}添加完歷史瀏覽記錄,我們在用戶中心頁獲取一下歷史瀏覽記錄。這里需要說明的是,我們只獲取前五條歷史瀏覽記錄。代碼如下:
//獲取歷史瀏覽記錄var goods []models.GoodsSKUconn,_ :=redis.Dial("tcp",":6379")reply,err := conn.Do("lrange","history"+strconv.Itoa(user.Id),0,4)replyInts,_ := redis.Ints(reply,err)for _,val := range replyInts{var temp models.GoodsSKUo.QueryTable("GoodsSKU").Filter("Id",val).One(&temp)goods = append(goods, temp)}this.Data["goods"] = goods顯示歷史紀錄的視圖代碼如下:
{{if .goods}}{{range .goods}}<li><a href="detail.html"><img src="http://192.168.110.81:8888/{{.Image}}"></a><h4><a href="detail.html">{{.Name}}</a></h4><div class="operate"><span class="prize">¥{{.Price}}</span><span class="unit">{{.Price}}/{{.Unite}}</span><a href="#" class="add_goods" title="加入購物車"></a></div></li>{{end}} {{else}}<li>無歷史瀏覽記錄</li> {{end}}
-
3.列表頁商品內容展示
請求
列表頁展示的是相應類型的所有數據,所以請求應該是點擊類型的時候跳轉的頁面,需要傳遞類型ID,所以我們要在類型超鏈接上添加請求路徑為/goodsList?typeId=1
路由
有了請求之后需要我們在路由文件中添加相對應的控制器和方法,代碼如下:
//列表頁展示 beego.Router("/goodsList",&controllers.GoodsController{},"get:ShowGoodsList")控制器
接著我們來實現ShowGoodsList方法.
-
獲取數據
首先我們要獲取到路由傳遞過來的類型ID
//獲取類型id typeId,err := this.GetInt("typeId") -
校驗數據
對獲取的數據進行校驗
//校驗數據 if err != nil{beego.Info("獲取類型ID錯誤")this.Redirect("/",302)return } -
處理數據
我們 這個頁面主要是去查詢相關的內容
由圖可知,我們需要獲取三部分數據
-
類型數據
獲取類型數據業務就比較簡單,直接把所有類型數據從數據庫中獲取到。代碼如下:
var types []models.GoodsType o.QueryTable("GoodsType").All(&types) this.Data["types"] = types -
獲取當前類型的商品數據
//獲取當前類型的商品 var goodsSKus []models.GoodsSKU o.QueryTable("GoodsSKU").RelatedSel("GoodsType").Filter("GoodsType__Id",typeId).All(&goodsSKus) this.Data["goods"] = goodsSKus -
獲取新品數據
我們這里獲取的是同類型,時間靠前的兩個商品數據,代碼如下:
//獲取兩個新品 var goodsNew []models.GoodsSKU o.QueryTable("GoodsSKU").RelatedSel("GoodsType").Filter("GoodsType__Id",typeId).OrderBy("Time").Limit(2,0).All(&goodsNew) this.Data["goodsNew"] = goodsNew
-
視圖
后臺獲取的數據,要在視圖中循環顯示:
<div class="navbar_con"><div class="navbar clearfix"><div class="subnav_con fl"><h1>全部商品分類</h1> <span></span> <ul class="subnav">{{range .types}}<li><a href="/goodsList?id={{.Id}}" class="{{.Logo}}">{{.Name}}</a></li>{{end}}</ul></div><ul class="navlist fl"><li><a href="">首頁</a></li><li class="interval">|</li><li><a href="">手機生鮮</a></li><li class="interval">|</li><li><a href="">抽獎</a></li></ul></div></div><div class="breadcrumb"><a href="#">全部分類</a><span>></span><a href="#">新鮮水果</a></div><div class="main_wrap clearfix"><div class="l_wrap fl clearfix"><div class="new_goods"><h3>新品推薦</h3><ul>{{range .goodsNew}}<li><a href="/goodsDetail?id={{.Id}}"><img src="http://192.168.110.81:8888/{{.Image}}"></a><h4><a href="/goodsDetail?id={{.Id}}">{{.Name}}</a></h4><div class="prize">¥{{.Price}}</div></li>{{end}}</ul></div></div><div class="r_wrap fr clearfix"><div class="sort_bar"><a href="/goodsList?id={{.typeId}}" class="active">默認</a><a href="#">價格</a><a href="#">人氣</a></div><ul class="goods_type_list clearfix">{{range .goods}}<li><a href="/goodsDetail?id={{.Id}}"><img src="http://192.168.110.81:8888/{{.Image}}"></a><h4><a href="/goodsDetail?id={{.Id}}">{{.Name}}</a></h4><div class="operate"><span class="prize">¥{{.Price}}</span><span class="unit">{{.Price}}/{{.Unite}}</span><a href="#" class="add_goods" title="加入購物車"></a></div></li>{{end}}</ul><div class="pagenation"><a href="#">上一頁</a><a href="#" class="active">1</a><a href="#">下一頁></a></div></div></div>分頁處理
我們這里實現的分頁是如下的效果:
所以這里我們還需要獲取相應頁碼,而且這個頁碼數,無論頁碼怎么變化,都顯示的五條,所以這里獲取五條頁碼就成了實現分頁重要的一個環節。
這個可以分四種情況分析。
第一種:總頁碼數不到五頁
這種情況下,要顯示所有頁碼
第二種:顯示的是前三頁的內容
這種顯示的都是1 2 3 4 5
第三種:顯示的是最后三頁的內容
顯示的都是后五頁的頁碼
第四種:處于中間的頁碼
分析出來的代碼如下:
if pageCount < 5{pageIndexBuffer = make([]int,pageCount)for index,_ := range pageIndexBuffer{pageIndexBuffer[index] = index + 1}}else if pageIndex < 3 {pageIndexBuffer = make([]int,5)for index,_ := range pageIndexBuffer{pageIndexBuffer[index] = index + 1}}else if pageIndex >= pageCount -3{pageIndexBuffer = make([]int,page)for index,_ := range pageIndexBuffer{pageIndexBuffer[index] = pageCount - 5 + index}}else {pageIndexBuffer = make([]int, 5)for index,_ := range pageIndexBuffer{pageIndexBuffer[index] = pageIndex - 3 + index}}由代碼分析可知,這里我們需要獲取總頁碼說pageCount,需要獲取當前頁碼pageIndex。那我們需要先獲取pageIndex和pageCount。
獲取總頁碼和當前頁碼
獲取總頁碼和當前頁碼的業務邏輯和以前我們獲取總頁碼和當前頁碼的邏輯一樣,代碼如下:
//分頁處理//指定每頁顯示多少數據pageSize := 2pageIndex ,err:=this.GetInt("pageIndex")if err !=nil{pageIndex = 1}start := pageSize * (pageIndex - 1)//處理頁碼count,_ := o.QueryTable("GoodsSKU").RelatedSel("GoodsType").Filter("GoodsType__Id",typeId).Count()//獲取總頁碼pageCount := math.Ceil(float64(count)/ float64(pageSize))獲取顯示的頁碼
這里我們可以封裝一個獲取相應頁碼的函數,需要把總頁碼,當前頁碼,和要顯示多少個頁碼當參數傳遞,代碼如下:
//指定顯示多少個頁碼 page := 5 //函數定義如下//分頁助手函數 func PageTool(pageCount int,page int,pageIndex int)map[string]interface{}{//獲取應該顯示的頁碼var pageIndexBuffer []intif pageCount < page{pageIndexBuffer = make([]int,pageCount)for index,_ := range pageIndexBuffer{pageIndexBuffer[index] = index + 1}}else if pageIndex < ( page + 1)/2 {pageIndexBuffer = make([]int,page)for index,_ := range pageIndexBuffer{pageIndexBuffer[index] = index + 1}}else if pageIndex >= pageCount -( page + 1)/2{pageIndexBuffer = make([]int,page)for index,_ := range pageIndexBuffer{pageIndexBuffer[index] = pageCount - page + index}}else {pageIndexBuffer = make([]int, page)for index,_ := range pageIndexBuffer{pageIndexBuffer[index] = pageIndex - (page - 1)/2 + index}}//上一頁頁碼pagePre := pageIndex - 1if pageIndex == 1{pagePre = 1}pageNext := pageIndex + 1if pageIndex == pageCount{pageNext = pageIndex}//把數據返回pageData := make(map[string]interface{})pageData["pagePre"] = pagePrepageData["pageNext"] = pageNextpageData["pageIndex"] = pageIndexBufferreturn pageData }注意:這里上一頁和下一頁的處理也放到這個函數中實現
獲取到數據之后,我們要把相應的數據傳遞給視圖:
//把數據傳遞給視圖this.Data["pagePre"] = pageData["pagePre"]this.Data["pageNext"] = pageData["pageNext"]this.Data["pages"] = pageData["pageIndex"].([]int)this.Data["pageIndex"] = pageIndex在視圖中顯示:
<a href="/goodsList?pageIndex={{.pagePre}}">上一頁</a> {{range $index,$val := .pages}}{{if compare $.pageIndex $val}}<a href="/goodsList?pageIndex={{$val}}" class="active">{{$val}}</a>{{else}}<a href="/goodsList?pageIndex={{$val}}">{{$val}}</a>{{end}} {{end}} <a href="/goodsList?pageIndex={{.pageNext}}">下一頁></a>感覺好像成功了,有沒有問題呢?我們接著往下看
獲取分頁數據
這時候我們可以去獲取分頁的數據了,獲取分頁的數據依舊用高級查詢中的Limit函數,代碼如下:
o.QueryTable("GoodsSKU").RelatedSel("GoodsType").Filter("GoodsType__Id",typeId).Limit(pageSize,start).All(&goodsSKus)注意:這時候你會遇見一個問題,當直接訪問列表頁的時候,數據正確,但是點擊上一頁,下一頁的時候,你會發現,獲取數據不正確。什么原因呢?其實根據錯誤提示也能發現,我們點擊上一頁下一頁以及相應頁碼的時候,并沒有傳遞typeId值過來,所以對于后臺來說,在新的請求中并不知道這次你要獲取哪個類型的數據。這時候我們需要在上一次的訪問中把typeId傳遞給視圖。然后在分頁的標簽傳遞回來。具體代碼實現如下:
先把typeId傳遞回視圖
this.Data["typeId"] = typeId在視圖中拼接標簽,傳遞回來typeId內容
<a href="/goodsList?typeId={{.typeId}}&pageIndex={{.pagePre}}">上一頁</a> {{range $index,$val := .pages}}{{if compare $.pageIndex $val}}<a href="/goodsList?typeId={{$.typeId}}&pageIndex={{$val}}" class="active">{{$val}}</a>{{else}}<a href="/goodsList?typeId={{$.typeId}}&pageIndex={{$val}}">{{$val}}</a>{{end}} {{end}} <a href="/goodsList?typeId={{.typeId}}&pageIndex={{.pageNext}}">下一頁</a>這時候,分頁的功能就實現了 。
根據不同的選項獲取不同排序內容
由圖可知,我們這個頁面還要根據不同的選擇,顯示不同的商品排序,分別是默認排序,價格排序,人氣(銷量)排序。那就需要在我們查詢的時候再加上排序的函數,這里我們用的排序函數是OrderBy(),也是高級查詢函數,參數是要排序的字段名。那這時候,我們給這三個超鏈接就需要添加一個sort的值。代碼如下:
<a href="/goodsList?typeId={{.typeId}}" class="active">默認</a> <a href="/goodsList?typeId={{.typeId}}&sort=price">價格</a> <a href="/goodsList?typeId={{.typeId}}&sort=sale">人氣</a>在后臺,我們需要根據不同的sort值獲取不同順序的數據,代碼如下:
//根據不同的選項獲取不同的數據 sort := this.GetString("sort") //如果sort等于空,就按照默認排序 if sort == ""{ o.QueryTable("GoodsSKU").RelatedSel("GoodsType").Filter("GoodsType__Id",typeId).Limit(pageSize,start).All(&goodsSKus) }else if sort == "price"{ o.QueryTable("GoodsSKU").RelatedSel("GoodsType").Filter("GoodsType__Id",typeId).OrderBy("Price").Limit(pageSize,start).All(&goodsSKus) }else{ o.QueryTable("GoodsSKU").RelatedSel("GoodsType").Filter("GoodsType__Id",typeId).OrderBy("Sales").Limit(pageSize,start).All(&goodsSKus) }4.搜索頁面商品內容展示
ePre}}">上一頁
{{range index,index,index,val := .pages}}
{{if compare $.pageIndex KaTeX parse error: Expected 'EOF', got '}' at position 4: val}?} <a href="/g….typeId}}&pageIndex={{KaTeX parse error: Expected 'EOF', got '}' at position 4: val}?}" class="activ…val}}
{{else}}
{{$val}}
{{end}}
{{end}}
下一頁
在后臺,我們需要根據不同的sort值獲取不同順序的數據,代碼如下:
//根據不同的選項獲取不同的數據 sort := this.GetString("sort") //如果sort等于空,就按照默認排序 if sort == ""{ o.QueryTable("GoodsSKU").RelatedSel("GoodsType").Filter("GoodsType__Id",typeId).Limit(pageSize,start).All(&goodsSKus) }else if sort == "price"{ o.QueryTable("GoodsSKU").RelatedSel("GoodsType").Filter("GoodsType__Id",typeId).OrderBy("Price").Limit(pageSize,start).All(&goodsSKus) }else{ o.QueryTable("GoodsSKU").RelatedSel("GoodsType").Filter("GoodsType__Id",typeId).OrderBy("Sales").Limit(pageSize,start).All(&goodsSKus) }4.搜索頁面商品內容展示
總結
以上是生活随笔為你收集整理的GO语言开发天天生鲜项目第四天 商品后台管理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 怎么批量选择目标_全日制专升本一次可以报
- 下一篇: csi python 摄像头 树莓派_树