GO语言开发天天生鲜项目第四天 商品后台管理
商品模塊
一:商品后臺管理
我們添加商品的后臺和我們前面做的案例后臺管理很類似,這里我們就直接拿我們前面的案例來改一下,直接當(dāng)我們商品后臺管理
1.代碼遷移
-
先把我們案例文件夾下的views文件夾刪除。
-
把課堂資料中的views.zip解壓到我們的classOne下面
-
在GoLand中打開項(xiàng)目
-
把項(xiàng)目中model.go文件里面的代碼改為我們天天生鮮的model.go內(nèi)容(修改數(shù)據(jù)庫)
-
修改user.go和article.go中報(bào)紅的地方
-
控制頁面顯示的函數(shù)留下修改,處理業(yè)務(wù)的函數(shù)都注釋掉(因?yàn)樾枰幚淼臉I(yè)務(wù)可能會(huì)發(fā)生變化,所以先注釋掉)
-
需要做的修改就是把原來表的名字和字段修改為新的表的名字和字段
-
2.登陸和注冊
注冊業(yè)務(wù)沒有什么改變,需要注意的是一些字段改變的不一樣啦,還有就是我們在后臺注冊和在天天生鮮界面注冊的權(quán)限不一致。
登陸的時(shí)候判斷也基本不變,需要注意的是要多添加一塊權(quán)限驗(yàn)證:
//5.判斷是否權(quán)限登陸后臺管理,如果不等于1說明是普通用戶,不能登陸后臺if user.Power != 1{beego.Info("沒有權(quán)限登陸后臺")this.TplName = "login.html"return}登陸成功之后跳轉(zhuǎn)到列表頁:
進(jìn)入到后臺之后我們先來實(shí)現(xiàn)增加類型業(yè)務(wù)。
3.添加類型
添加類型界面分兩塊,一塊是類型信息的展示,一塊是類型的添加
3.1.添加類型
3.1.1獲取數(shù)據(jù)
我們獲取的時(shí)候發(fā)現(xiàn),添加類型需要上傳兩張圖片,上傳圖片功能,我們在添加文章函數(shù)中用過。但是如果每有一處上傳圖片的業(yè)務(wù)就得寫一次這個(gè)代碼,代碼就重復(fù)了,針對重復(fù)代碼,我們可以抽離出來一個(gè)上傳圖片的函數(shù)
3.1.2抽離上傳圖片函數(shù)
把添加文章中關(guān)于上傳文件的代碼抽離出來,先不指定參數(shù)和返回值:
//抽象上傳圖片函數(shù) func UploadImage(){//1.那數(shù)據(jù)//那標(biāo)題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 } }然后發(fā)現(xiàn),函數(shù)功能里面需要兩個(gè)參數(shù),一個(gè)是控制器本身,因?yàn)楹芏郻eego的函數(shù)需要有beego的控制器才能調(diào)用。另外一個(gè)是上傳文件時(shí),需要提供的name屬性。因?yàn)椴煌奈募蟼鲗?yīng)的name值也可能不一樣。多以我們給這個(gè)函數(shù)添加兩個(gè)參數(shù)。上傳完文件之后,我們還需要獲取到文件的 存儲(chǔ)路徑,所以需要函數(shù)返回一個(gè)路徑,這里我們給函數(shù)加一個(gè)string類型的返回值。函數(shù)完整代碼如下:
func UploadImage(this*beego.Controller,filePath string)string{//1.那數(shù)據(jù)//那標(biāo)題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 }有了上傳圖片函數(shù)之后,添加類型獲取數(shù)據(jù)代碼如下:
//1.獲取數(shù)據(jù)typename:=this.GetString("typeName")logoImage := UploadImage(&this.Controller,"uploadlogo")typeImage :=UploadImage(&this.Controller,"uploadTypeImage")3.1.2.數(shù)據(jù)校驗(yàn)
對獲取的數(shù)據(jù)判斷一下,看是否為空:
//判斷數(shù)據(jù)if typename == ""||logoImage == "" || typeImage == ""{beego.Info("添加類型數(shù)據(jù)為空")return}#####3.1.3.插入數(shù)據(jù)庫
數(shù)據(jù)沒問題的話,就獲取一個(gè)GoodsType對象并給它賦值,然后插入數(shù)據(jù)庫:
//執(zhí)行插入操作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.返回視圖
業(yè)務(wù)執(zhí)行完之后還是返回到本頁面,看一下是否添加成功
this.Redirect("/Article/AddArticleType",302)3.2.利用fastDFS存儲(chǔ)圖片
3.2.1什么是FastDFS
FastDFS 是用 c 語言編寫的一款開源的分布式文件系統(tǒng)。FastDFS 為互聯(lián)網(wǎng)量身定制, 充分考慮了冗余備份、負(fù)載均衡、線性擴(kuò)容等機(jī)制,并注重高可用、高性能等指標(biāo),使用 FastDFS 很容易搭建一套高性能的文件服務(wù)器集群提供文件上傳、下載等服務(wù)。
優(yōu)點(diǎn):
FastDFS 架構(gòu)包括 Tracker server 和 Storage server。客戶端請求 Tracker server 進(jìn)行文 件上傳、下載,通過 Tracker server 調(diào)度最終由 Storage server 完成文件上傳和下載。
Tracker server 作用是負(fù)載均衡和調(diào)度,通過 Tracker server 在文件上傳時(shí)可以根據(jù)一些 方法找到 Storage server 提供文件上傳服務(wù)。可以將 tracker 稱為追蹤服務(wù)器或調(diào)度服務(wù) 器。
Storage server 作用是文件存儲(chǔ),客戶端上傳的文件最終存儲(chǔ)在 Storage 服務(wù)器上, Storageserver 沒有實(shí)現(xiàn)自己的文件系統(tǒng)而是利用操作系統(tǒng) 的文件系統(tǒng)來管理文件。可以將 storage 稱為存儲(chǔ)服務(wù)器。
服務(wù)端兩個(gè)角色:
Tracker:管理集群,tracker 也可以實(shí)現(xiàn)集群。每個(gè) tracker 節(jié)點(diǎn)地位平等。收集 Storage 集群的狀態(tài)。
Storage:實(shí)際保存文件
Storage 分為多個(gè)組,每個(gè)組之間保存的文件是不同的。每 個(gè)組內(nèi)部可以有多個(gè)成員,組成員內(nèi)部保存的內(nèi)容是一樣的,組成員的地位是一致的,沒有 主從的概念。
3.2.2文件上傳流程
客戶端上傳文件后存儲(chǔ)服務(wù)器將文件 ID 返回給客戶端,此文件 ID 用于以后訪問該文 件的索引信息。文件索引信息包括:組名,虛擬磁盤路徑,數(shù)據(jù)兩級目錄,文件名。
組名: 文件上傳后所在的 storage 組名稱,在文件上傳成功后有 storage 服務(wù)器返回, 需要客戶端自行保存。
虛擬磁盤路徑: storage 配置的虛擬路徑,與磁盤選項(xiàng) store_path*對應(yīng)。如果配置了 store_path0 則是 M00,如果配置了 store_path1 則是 M01,以此類推。
**數(shù)據(jù)兩級目錄 **:storage 服務(wù)器在每個(gè)虛擬磁盤路徑下創(chuàng)建的兩級目錄,用于存儲(chǔ)數(shù)據(jù) 文件。
文件名 :與文件上傳時(shí)不同。是由存儲(chǔ)服務(wù)器根據(jù)特定信息生成,文件名包含:源存儲(chǔ) 服務(wù)器 IP 地址、文件創(chuàng)建時(shí)間戳、文件大小、隨機(jī)數(shù)和文件拓展名等信息。
3.2.3文件下載流程
3.2.4簡易FastDFS架構(gòu)
3.2.5FastDFS安裝
3.2.5.1安裝FastDFS依賴包
3.2.5.2安裝FastDFS
3.2.5.3配置跟蹤服務(wù)器tracker
在/home/itcast/目錄中創(chuàng)建目錄 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配置存儲(chǔ)服務(wù)器storage
在/home/itcast/fastdfs/ 目錄中創(chuàng)建目錄 storage
mkdir –p /itcast/python/fastdfs/storage編輯/etc/fdfs/storage.conf配置文件 sudo vim /etc/fdfs/storage.conf
修改內(nèi)容:
base_path=/home/itcast/fastdfs/storage store_path0=/home/itcast/fastdfs/storage tracker_server=自己ubuntu虛擬機(jī)的ip地址:221223.2.5.5啟動(dòng)tracker和storage
進(jìn)入到/etc/fdfs/下面執(zhí)行以下兩條指令
sudo fdfs_trackerd /etc/fdfs/tracker.conf sudo fdfs_storaged /etc/fdfs/storage.conf3.2.5.6測試是否安裝成功
修改內(nèi)容:
base_path=/home/python/fastdfs/tracker tracker_server=自己ubuntu虛擬機(jī)的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
進(jìn)入nginx-1.8.1目錄中
執(zhí)行
sudo ./configure --prefix=/usr/local/nginx/ --add-module=fastdfs-nginx-module-master解壓后的目錄的絕對路徑/src注意:這時(shí)候會(huì)報(bào)一個(gè)錯(cuò),說沒有PCRE庫
下載缺少的庫
sudo apt-get install libpcre3 libpcre3-dev-
首先你需要去更換源,因?yàn)閡buntu自帶的源沒有這個(gè)庫
-
更換下載源為阿里的源
-
先把原來的源文件備份
sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak -
編輯源文件
sudo vim /etc/apt/sources.list把原來的內(nèi)容全部刪掉,粘貼一下內(nèi)容:
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更換完源之后執(zhí)行
sudo apt-get update sudo apt-get install libpcre3 libpcre3-dev然后進(jìn)入nginx-1.8.1目錄中,再次執(zhí)行:
sudo ./configure --prefix=/usr/local/nginx/ --add-module=fastdfs-nginx-module-master解壓后的目錄的絕對路徑/src這時(shí)候還會(huì)報(bào)一個(gè)錯(cuò)(錯(cuò)誤還真多),錯(cuò)誤原因是因?yàn)閚ginx編譯的時(shí)候把警告當(dāng)錯(cuò)誤處理,事實(shí)上這個(gè)警告并不影響(程序員忽略警告):
解決方法:
找到objs目錄下的Makefile
vim Makefile
刪掉里面的-Werror(如果沒有修改權(quán)限,修改一下這個(gè)文件的權(quán)限,chmod 777 Makefile)
然后回到nginx-1.8.1目錄中,再次執(zhí)行:
sudo ./configure --prefix=/usr/local/nginx/ --add-module=fastdfs-nginx-module-master解壓后的目錄的絕對路徑/src執(zhí)行完成后執(zhí)行sudo make
執(zhí)行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
修改內(nèi)容:
connect_timeout=10 tracker_server=自己ubuntu虛擬機(jī)的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;}}啟動(dòng)nginx
sudo /usr/local/nginx/sbin/nginx
3.2.6使用go客戶端上傳文件測試
-
下載包
go get github.com/weilaihui/fdfs_client這時(shí)候會(huì)報(bào)一個(gè)錯(cuò):
這是因?yàn)槲覀兊木W(wǎng)絡(luò)有防火墻,不能直接去google下載相應(yīng)的包,所以就失敗啦
解決辦法:
-
在~/workspace/go/src目錄下面創(chuàng)建一個(gè)golang.org/x目錄
cd ~/workspace/go/src mkdir -p golang.org/x -
進(jìn)入golang.org/x下載兩個(gè)包
cd golang.org/x git clone https://github.com/golang/crypto.git git clone https://github.com/golang/sys.git -
然后再執(zhí)行最初的下載命令
go get github.com/weilaihui/fdfs_client
-
-
go操作fastDFS的方法
-
先導(dǎo)包,把我們下載的包導(dǎo)入
import "github.com/weilaihui/fdfs_client" -
導(dǎo)包之后,我們需要指定配置文件生成客戶端對象
client,_:=fdfs_client.NewFdfsClient("/etc/fdfs/client.conf") -
接著我們就可以通過client對象執(zhí)行文件上傳,上傳有兩種方法,一種是通過文件名,一種是通過字節(jié)流
-
通過文件名上傳**UploadByFilename **,參數(shù)是文件名(必須通過文件名能找到要上傳的文件),返回值是fastDFS定義的一個(gè)結(jié)構(gòu)體,包含組名和文件ID兩部分內(nèi)容
fdfsresponse,err := client.UploadByFilename("flieName") -
通過字節(jié)流上傳UploadByBuffer,參數(shù)是字節(jié)數(shù)組和文件后綴,返回值和通過文件名上傳一樣。
fdfsresponse,err := client.UploadByBuffer(fileBuffer,ext)
-
-
3.3.改用fastDFS上傳文件
? 我們以前保存視圖傳遞過來的文件方法是先用GetFile()獲取文件相關(guān)信息,然后再用SaveToFile()把文件保存到 文件夾下面。以前保存圖片的代碼如下:
func UploadImage(this*beego.Controller,filePath string)string{//1.那數(shù)據(jù)//那標(biāo)題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 }我們現(xiàn)在要用fastDFS來存儲(chǔ)圖片,第一步也是先用GetFile拿到文件,但是第二步,我們用UploadByBuffer()把靜態(tài)文件存到我們fastDFS文件系統(tǒng)中。
//先導(dǎo)包 import "github.com/weilaihui/fdfs_client"//通過GetFile獲取文件信息 f,h,err := this.GetFile(filePath) defer f.Close() //然后對上傳的文件進(jìn)行格式和大小判斷 //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.上傳文件 //先獲取一個(gè)[]byte fileBuffer := make([]byte,h.Size) //把文件數(shù)據(jù)讀入到fileBuffer中 f.Read(fileBuffer) //獲取client對象 client := fdfs_client.NewFdfsClient("/etc/fdfs/client.conf") //上傳 fdfsresponse,_:=client.UploadByBuffer(fileBuffer,ext) //返回文件ID return fdfsresponse.RemoteFileId上傳圖片方式修改完成之后我們添加類型就業(yè)務(wù)就結(jié)束了。
3.4 類型展示
類型展示和案例中的展示業(yè)務(wù)一樣,只是一些表名和字段名需要簡單修改,代碼如下:
//1.讀取類型表,顯示數(shù)據(jù)o := orm.NewOrm()var goodsTypes[]models.GoodsType//查詢_,err:=o.QueryTable("GoodsType").All(&goodsTypes)if err != nil{beego.Info("查詢類型錯(cuò)誤")}this.Data["title"] = "添加類型"this.Data["goodsTypes"] = goodsTypesthis.Layout = "layout.html"this.TplName = "addType.html"到這里,我們添加類型界面就算完成了。但是我們后面還需要添加goodsSPU,添加商品,刪除商品,修改商品等功能和我們以前寫的業(yè)務(wù)基本一樣,老師就不給你們寫了,你們自己去實(shí)現(xiàn)一下相關(guān)業(yè)務(wù)。
有的學(xué)生覺得我們這塊和以前寫的一樣,覺得沒有意思,我們這里就直接導(dǎo)入數(shù)據(jù),不再一步步的添加數(shù)據(jù)
3.5導(dǎo)入商品有關(guān)數(shù)據(jù)
3.5.1導(dǎo)入數(shù)據(jù)庫數(shù)據(jù)
? 導(dǎo)入數(shù)據(jù)庫數(shù)據(jù),老師給你們的資料中,有一個(gè)文件是 dailyfresh.sql,就是我以前導(dǎo)入的數(shù)據(jù),你們可以把這個(gè)sql語句導(dǎo)入到你們的數(shù)據(jù)庫中,直接拿來使用。
-
先進(jìn)入數(shù)據(jù)庫中
mysql -uroot -p123456 -
選中項(xiàng)目中用到的數(shù)據(jù)庫
use dailyfresh -
導(dǎo)入文件(保證dailyfresh.sql文件在你當(dāng)前目錄下面)
source dailyfresh.sql -
查看數(shù)據(jù)是否導(dǎo)入成功
select * from goods_type;
3.5.2導(dǎo)入圖片數(shù)據(jù)
? 導(dǎo)入數(shù)據(jù)庫數(shù)據(jù)之后我們還有很多圖片內(nèi)容是存在fastDFS中的,也需要我們手動(dòng)導(dǎo)入
-
刪除 ~/fdfs/storage/data/00目錄下的00文件夾
-
把課堂資料中的00.zip拷貝到我們存放圖片的路徑下面 ~/fdfs/storage/data/00
-
解壓00.zip到當(dāng)前目錄
這時(shí)候我們就把數(shù)據(jù)全部導(dǎo)入到了我們的開發(fā)環(huán)境當(dāng)中。
二.商品信息展示
前面我們導(dǎo)入了全部商品有關(guān)的數(shù)據(jù),接著我們就來實(shí)現(xiàn),商品展示有關(guān)的內(nèi)容。
1.首頁內(nèi)容的展示
首先我們來看一下首頁的頁面。
商品頁面分為如圖所示的四部分,那我們就來分別獲取這四部分的值,仍舊按照我們的四步驟來走。
請求
這里我們設(shè)定首頁的請求路徑為\
路由
然后我們?nèi)outer.go中修改相應(yīng)的路由適配
//顯示主頁面beego.Router("/",&controllers.GoodsController{},"get:ShowIndex")**控制器 **
這里我們指定首頁對應(yīng)GoodsController控制器,獲取首頁的方法為ShowIndex,接著我們就來實(shí)現(xiàn)這個(gè)方法。
-
獲取類型
//查詢商品類型var goodsTypes []models.GoodsTypeo.QueryTable("GoodsType").All(&goodsTypes)this.Data["types"] = goodsTypes -
獲取輪播商品數(shù)據(jù)
//查詢輪播商品圖片var goodsBanner []models.IndexGoodsBannero.QueryTable("IndexGoodsBanner").OrderBy("Index").All(&goodsBanner)this.Data["goodsBanners"] = goodsBanner -
查詢促銷商品數(shù)據(jù)
//查詢促銷商品var promotionBanner []models.IndexPromotionBannero.QueryTable("IndexPromotionBanner").OrderBy("Index").All(&promotionBanner)this.Data["proBanner"] = promotionBanner -
查詢首頁商品
這個(gè)業(yè)務(wù)比較復(fù)雜,我們觀察頁面發(fā)現(xiàn)我們查詢的時(shí)候要把類型對象和相應(yīng)的商品對象切片放在一起,那我們用什么容器呢?分析如下圖:
分析之后我們發(fā)現(xiàn),只能用interface這種類型,還需要給不同類型的數(shù)據(jù)加一個(gè)key值,所以我們需要用map[string]interface{},這是一個(gè) 類型的內(nèi)容,那所有類型的內(nèi)容,我們應(yīng)該給這個(gè)類型放到切片中,所以我們這里用**[]map[string]interface{}**這種類型來存儲(chǔ)首頁商品。
確定了存儲(chǔ)容器之后,我們開始查詢數(shù)據(jù)來存儲(chǔ),首先是把商品類型存儲(chǔ)到我們的容器當(dāng)中:
goods := make([]map[string]interface{},len(goodsTypes))for index,goodsType := range goodsTypes{temp := make(map[string]interface{})temp["type"] = goodsTypegoods[index] = temp}接著把商品存儲(chǔ)到我們的容器中,注意這里是分為圖片商品和文字商品,需要分開來存儲(chǔ):
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 }然后再把數(shù)據(jù)傳遞給視圖
this.Data["goods"] = goods
**視圖 **
? 獲取完數(shù)據(jù)就需要在頁面里面顯示,這里注意,我們獲取的圖片數(shù)據(jù)是FastDFS返回的ID值,這里我們訪問的是通過nginx獲取的圖片數(shù)據(jù),所以需要在圖片地址上需要加上相應(yīng)的ip和端口號,代碼如下:
<div class="center_con clearfix"><ul class="subnav fl"><!--------循環(huán)展示類型數(shù)據(jù)---------->{{range .types}}<li><a href="" class="{{.Logo}}">{{.Name}}</a></li>{{end}}</ul><div class="slide fl"><ul class="slide_pics"><!--------循環(huán)展示輪播圖---------->{{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"><!--------循環(huán)展示促銷商品數(shù)據(jù)---------->{{range .proBanner}}<a href="{{.Url}}"><img src="http://192.168.110.81:8888/{{.Image}}"></a>{{end}}</div></div> <!--------循環(huán)展示首頁商品----------> {{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><!--------循環(huán)展示文字商品數(shù)據(jù)---------->{{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"><!--------循環(huán)展示圖片商品數(shù)據(jù)---------->{{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.商品詳情頁的展示
首頁展示之后,我們接著來實(shí)現(xiàn)商品詳情頁的展示!仍然是我們你的四步驟
請求
商品詳情頁的請求應(yīng)該在首頁展示的相應(yīng)頁面添加,不管是文字商品還是圖片商品都應(yīng)該添加。商品詳情和我們實(shí)現(xiàn)案例中文章詳情一樣,都需要傳遞相應(yīng)的Id,所以我們設(shè)計(jì)商品詳情的路由為
/goodsDetail?id={{.GoodsSKU.Id}}
路由
然后到router.go中添加對應(yīng)的路由
beego.Router("/goodsDetail",&controllers.GoodsController{},"get:ShowDetail")控制器
接著我們來實(shí)現(xiàn)ShowDetail方法。仍然是我們的四步驟。
-
獲取數(shù)據(jù)
id,err:= this.GetInt("id") -
校驗(yàn)數(shù)據(jù)
if err != nil{beego.Error("獲取數(shù)據(jù)不存在")this.Redirect("/",302)return } -
數(shù)據(jù)處理
首先我們看一下,我們這個(gè)頁面:
由頁面可知,我們要獲取類型數(shù)據(jù)和商品數(shù)據(jù)。代碼如下:
//獲取商品類型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
視圖
我們把獲取到的商品詳細(xì)信息添加在視圖中顯示,代碼如下:
<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">數(shù) 量:</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">總價(jià):<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>歷史記錄的添加和顯示
-
獲取詳情的時(shí)候我們需要添加歷史瀏覽記錄,我們介紹項(xiàng)目的時(shí)候就說過,我們的歷史瀏覽記錄存儲(chǔ)在redis中。這里我們就需要做一個(gè)歷史瀏覽記錄的存儲(chǔ)。在實(shí)現(xiàn)相關(guān)代碼之前,我們先來分析一下,我們?nèi)绾稳ゴ鎯?chǔ) 歷史瀏覽記錄。這里有以下幾個(gè)問題。
-
1.什么時(shí)候添加歷史瀏覽記錄
在登陸的情況下查看商品詳情的時(shí)候添加
-
2.什么時(shí)候獲取歷史瀏覽記錄
在用戶中心頁獲取歷史瀏覽記錄
-
3.用什么格式來存儲(chǔ)用戶瀏覽記錄
這里我們要考慮一下歷史瀏覽記錄都存儲(chǔ)哪些內(nèi)容,歷史瀏覽記錄需要存儲(chǔ)哪個(gè)用戶瀏覽了哪些內(nèi)容,而且還有先后順序,所以要存儲(chǔ)用戶,商品內(nèi)容,而且用戶和商品是對應(yīng)存儲(chǔ),并且有順序,這里我們用redis數(shù)據(jù)庫中的list來存儲(chǔ)。存入的key值設(shè)定為history_用戶id,value值為商品id,存儲(chǔ)代碼如下:
//添加歷史瀏覽記錄,需要先查詢有沒有登陸,只有登陸之后可以添加歷史瀏覽記錄userName := this.GetSession("userName")if userName != nil{//查詢用戶信息var user models.Useruser.Name = userName.(string)o.Read(&user,"Name")conn,_:=redis.Dial("tcp",":6379")//插入歷史紀(jì)錄reply,err:=conn.Do("lpush","history"+strconv.Itoa(user.Id),id)reply,_ = redis.Bool(reply,err)if reply == false{beego.Info("插入瀏覽數(shù)據(jù)錯(cuò)誤")}}這時(shí)候需要注意,當(dāng)用戶多次瀏覽某個(gè)商品的時(shí)候,我們添加歷史瀏覽記錄只添加一條,所以在添加歷史瀏覽記錄之前需要把原來的記錄給清空。修改之后的代碼如下:
//添加歷史瀏覽記錄,需要先查詢有沒有登陸,只有登陸之后可以添加歷史瀏覽記錄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("插入瀏覽數(shù)據(jù)錯(cuò)誤")}//插入歷史紀(jì)錄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顯示歷史紀(jì)錄的視圖代碼如下:
{{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.列表頁商品內(nèi)容展示
請求
列表頁展示的是相應(yīng)類型的所有數(shù)據(jù),所以請求應(yīng)該是點(diǎn)擊類型的時(shí)候跳轉(zhuǎn)的頁面,需要傳遞類型ID,所以我們要在類型超鏈接上添加請求路徑為/goodsList?typeId=1
路由
有了請求之后需要我們在路由文件中添加相對應(yīng)的控制器和方法,代碼如下:
//列表頁展示 beego.Router("/goodsList",&controllers.GoodsController{},"get:ShowGoodsList")控制器
接著我們來實(shí)現(xiàn)ShowGoodsList方法.
-
獲取數(shù)據(jù)
首先我們要獲取到路由傳遞過來的類型ID
//獲取類型id typeId,err := this.GetInt("typeId") -
校驗(yàn)數(shù)據(jù)
對獲取的數(shù)據(jù)進(jìn)行校驗(yàn)
//校驗(yàn)數(shù)據(jù) if err != nil{beego.Info("獲取類型ID錯(cuò)誤")this.Redirect("/",302)return } -
處理數(shù)據(jù)
我們 這個(gè)頁面主要是去查詢相關(guān)的內(nèi)容
由圖可知,我們需要獲取三部分?jǐn)?shù)據(jù)
-
類型數(shù)據(jù)
獲取類型數(shù)據(jù)業(yè)務(wù)就比較簡單,直接把所有類型數(shù)據(jù)從數(shù)據(jù)庫中獲取到。代碼如下:
var types []models.GoodsType o.QueryTable("GoodsType").All(&types) this.Data["types"] = types -
獲取當(dāng)前類型的商品數(shù)據(jù)
//獲取當(dāng)前類型的商品 var goodsSKus []models.GoodsSKU o.QueryTable("GoodsSKU").RelatedSel("GoodsType").Filter("GoodsType__Id",typeId).All(&goodsSKus) this.Data["goods"] = goodsSKus -
獲取新品數(shù)據(jù)
我們這里獲取的是同類型,時(shí)間靠前的兩個(gè)商品數(shù)據(jù),代碼如下:
//獲取兩個(gè)新品 var goodsNew []models.GoodsSKU o.QueryTable("GoodsSKU").RelatedSel("GoodsType").Filter("GoodsType__Id",typeId).OrderBy("Time").Limit(2,0).All(&goodsNew) this.Data["goodsNew"] = goodsNew
-
視圖
后臺獲取的數(shù)據(jù),要在視圖中循環(huán)顯示:
<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="">手機(jī)生鮮</a></li><li class="interval">|</li><li><a href="">抽獎(jiǎng)</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">默認(rèn)</a><a href="#">價(jià)格</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>分頁處理
我們這里實(shí)現(xiàn)的分頁是如下的效果:
所以這里我們還需要獲取相應(yīng)頁碼,而且這個(gè)頁碼數(shù),無論頁碼怎么變化,都顯示的五條,所以這里獲取五條頁碼就成了實(shí)現(xiàn)分頁重要的一個(gè)環(huán)節(jié)。
這個(gè)可以分四種情況分析。
第一種:總頁碼數(shù)不到五頁
這種情況下,要顯示所有頁碼
第二種:顯示的是前三頁的內(nèi)容
這種顯示的都是1 2 3 4 5
第三種:顯示的是最后三頁的內(nèi)容
顯示的都是后五頁的頁碼
第四種:處于中間的頁碼
分析出來的代碼如下:
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,需要獲取當(dāng)前頁碼pageIndex。那我們需要先獲取pageIndex和pageCount。
獲取總頁碼和當(dāng)前頁碼
獲取總頁碼和當(dāng)前頁碼的業(yè)務(wù)邏輯和以前我們獲取總頁碼和當(dāng)前頁碼的邏輯一樣,代碼如下:
//分頁處理//指定每頁顯示多少數(shù)據(jù)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))獲取顯示的頁碼
這里我們可以封裝一個(gè)獲取相應(yīng)頁碼的函數(shù),需要把總頁碼,當(dāng)前頁碼,和要顯示多少個(gè)頁碼當(dāng)參數(shù)傳遞,代碼如下:
//指定顯示多少個(gè)頁碼 page := 5 //函數(shù)定義如下//分頁助手函數(shù) func PageTool(pageCount int,page int,pageIndex int)map[string]interface{}{//獲取應(yīng)該顯示的頁碼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}//把數(shù)據(jù)返回pageData := make(map[string]interface{})pageData["pagePre"] = pagePrepageData["pageNext"] = pageNextpageData["pageIndex"] = pageIndexBufferreturn pageData }注意:這里上一頁和下一頁的處理也放到這個(gè)函數(shù)中實(shí)現(xiàn)
獲取到數(shù)據(jù)之后,我們要把相應(yīng)的數(shù)據(jù)傳遞給視圖:
//把數(shù)據(jù)傳遞給視圖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>感覺好像成功了,有沒有問題呢?我們接著往下看
獲取分頁數(shù)據(jù)
這時(shí)候我們可以去獲取分頁的數(shù)據(jù)了,獲取分頁的數(shù)據(jù)依舊用高級查詢中的Limit函數(shù),代碼如下:
o.QueryTable("GoodsSKU").RelatedSel("GoodsType").Filter("GoodsType__Id",typeId).Limit(pageSize,start).All(&goodsSKus)注意:這時(shí)候你會(huì)遇見一個(gè)問題,當(dāng)直接訪問列表頁的時(shí)候,數(shù)據(jù)正確,但是點(diǎn)擊上一頁,下一頁的時(shí)候,你會(huì)發(fā)現(xiàn),獲取數(shù)據(jù)不正確。什么原因呢?其實(shí)根據(jù)錯(cuò)誤提示也能發(fā)現(xiàn),我們點(diǎn)擊上一頁下一頁以及相應(yīng)頁碼的時(shí)候,并沒有傳遞typeId值過來,所以對于后臺來說,在新的請求中并不知道這次你要獲取哪個(gè)類型的數(shù)據(jù)。這時(shí)候我們需要在上一次的訪問中把typeId傳遞給視圖。然后在分頁的標(biāo)簽傳遞回來。具體代碼實(shí)現(xiàn)如下:
先把typeId傳遞回視圖
this.Data["typeId"] = typeId在視圖中拼接標(biāo)簽,傳遞回來typeId內(nèi)容
<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>這時(shí)候,分頁的功能就實(shí)現(xiàn)了 。
根據(jù)不同的選項(xiàng)獲取不同排序內(nèi)容
由圖可知,我們這個(gè)頁面還要根據(jù)不同的選擇,顯示不同的商品排序,分別是默認(rèn)排序,價(jià)格排序,人氣(銷量)排序。那就需要在我們查詢的時(shí)候再加上排序的函數(shù),這里我們用的排序函數(shù)是OrderBy(),也是高級查詢函數(shù),參數(shù)是要排序的字段名。那這時(shí)候,我們給這三個(gè)超鏈接就需要添加一個(gè)sort的值。代碼如下:
<a href="/goodsList?typeId={{.typeId}}" class="active">默認(rèn)</a> <a href="/goodsList?typeId={{.typeId}}&sort=price">價(jià)格</a> <a href="/goodsList?typeId={{.typeId}}&sort=sale">人氣</a>在后臺,我們需要根據(jù)不同的sort值獲取不同順序的數(shù)據(jù),代碼如下:
//根據(jù)不同的選項(xiàng)獲取不同的數(shù)據(jù) sort := this.GetString("sort") //如果sort等于空,就按照默認(rèn)排序 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.搜索頁面商品內(nèi)容展示
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}}
下一頁
在后臺,我們需要根據(jù)不同的sort值獲取不同順序的數(shù)據(jù),代碼如下:
//根據(jù)不同的選項(xiàng)獲取不同的數(shù)據(jù) sort := this.GetString("sort") //如果sort等于空,就按照默認(rèn)排序 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.搜索頁面商品內(nèi)容展示
總結(jié)
以上是生活随笔為你收集整理的GO语言开发天天生鲜项目第四天 商品后台管理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 怎么批量选择目标_全日制专升本一次可以报
- 下一篇: 几种常见排序算法时间复杂度