页面展示
登录页面
欢迎页
商品查询(目前我还没再mysql中添加数据,目前没有数据展示)
新增商品
一.项目搭建
- 新建项目:ego
- 项目结构如下
--ego
--src 所有go源码
--user 业务模块包,user自定义名称,表示业务
--User.go 编写结构体,当作实体
--UserDao.go 数据访问
--UserService.go 业务逻辑
--UserController.go 控制器
--static 所有静态文件
--js 脚本
--css 样式表
--images 图片
--view 页面
--main.go 程序入口
- 编写main.go,显示login.html页面(因为这是练习,html文件不需要自己去写)
package main
import (
"net/http"
"html/template"
)
func welcome(w http.ResponseWriter, r *http.Request) {
t, _ := template.ParseFiles("view/login.html")
t.Execute(w, nil)
}
func main() {
s := http.Server{Addr: ":80"}
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
http.HandleFunc("/", welcome)
s.ListenAndServe()
}
- 在浏览器输入http://localhost 测试登录页面是否显示正常
- 页面演示如下
三.Commons编写
- Commons中内容是所有模块都可能使用的内容
- 在Commons中提供EgoResult.go的文件,表示前后台交互数据模版,文件内容如下
package commons
//前后台数据交互模版
type EgoResult struct {
Status int
Data interface{}
Msg string
}
-
提供DBUtils的.go文件,里面封装了对数据访问的代码,一共四个函数,其中Dml表示增加,删除,修改,Dql表示查询,并对外提供访问权限
- Dql函数没有关闭数据库连接,需要调用房手动关闭
package commons import ( "database/sql" _ "github.com/go-sql-driver/mysql" "fmt" ) //数据库操作的三个对象 var ( db *sql.DB stmt *sql.Stmt rows *sql.Rows ) //打开数据库连接,不要忘记导入驱动包 func openConn()(err error){ //此处为等号,否则创建局部变量 db,err=sql.Open("mysql","root:smallming@tcp(localhost:3306)/ego") if err!=nil{ fmt.Println("连接失败",err) return } return nil } //关闭连接,首字母大写,需要跨包访问的 func CloseConn(){ if rows!=nil{ rows.Close() } if stmt!=nil{ stmt.Close() } if db!=nil{ db.Close() } } //执行DML新增,删除,修改操作 func Dml(sql string ,args ... interface{}) (int64,error){ err:=openConn() if err!=nil{ fmt.Println("执行DML时出现错误,打开连接失败") return 0,err } //此处也是等号 stmt,err=db.Prepare(sql) if err!=nil{ fmt.Println("执行DML时出现错误,预处理出现错误") return 0,err } //此处要有...表示切片,如果没有表示数组,会报错 result,err:=stmt.Exec(args...) if err!=nil{ fmt.Println("执行DML出现错误,执行错误") return 0,err } count,err:=result.RowsAffected() if err!=nil{ fmt.Println("执行DML出现错误,获取受影响行数错误") return 0,err } CloseConn()//关闭连接 return count,err } //执行DQL查询 func Dql(sql string,args ... interface{}) (*sql.Rows,error){ err:= openConn() if err!=nil{ fmt.Println("执行DQL出现错误,打开连接失败") return nil,err } //此处是等号 stmt,err=db.Prepare(sql) if err!=nil{ fmt.Println("执行DQL出现错误,预处理实现") return nil,err } //此处参数是切片 rows,err=stmt.Query(args...) if err!=nil{ fmt.Println("执行DQL出现错误,执行错误") return nil,err } //此处没有关闭,调用此函数要记得关闭连接 return rows,nil }
四.登录功能流程图
- 用户登录后判断帐号和密码是否正确
- 服务器端判断后返回EgoResult结构体对应的JSON数据,其中Status=200表示成功,成功后页面跳转到
/index
,Status只要不为200都表示登录失败,页面给出提示框告诉用户登录失败 - 流程图
五.登录代码实现
- 在src下新建user目录
- 在user下新建TbUser.go文件,编写实体
package user
//对应数据库中用户表
type TbUser struct {
//属性首字母大写:1. 要转换为json 2. 可能出现跨包访问
Id int64
Username string
Password string
Phone string
Email string
Created string
Updated string
}
- 在user下新建TbUserDao.go文件,编写数据访问代码
package user
import (
"commons"
"fmt"
)
//根据用户名和密码查询,如果返回值为nil表示查询失败,否则成功
func SelByUnPwdDao(un, pwd string) *TbUser{
sql := "select * from tb_user where username=? and password=? or email=? and password=? or phone=? and password=?"
rows,err:=commons.Dql(sql, un, pwd, un, pwd, un, pwd)
if err!=nil{
fmt.Println(err)
return nil
}
if rows.Next(){
user:=new(TbUser)
rows.Scan(&user.Id,&user.Username,&user.Password,&user.Phone,&user.Email,&user.Created,&user.Updated)
commons.CloseConn()
return user
}
return nil
}
- 在user下新建TbUserService.go 编写业务逻辑代码
package user
import "commons"
func LoginService(un,pwd string) (er commons.EgoResult){
u:=SelByUnPwdDao(un,pwd)
if u!=nil{
er.Status=200
}else{
er.Status=400
}
return
}
- 在user下新建TbUserController.go编写控制器代码
package user
import (
"net/http"
"encoding/json"
)
//所有user模块的handler
func UserHandler() {
http.HandleFunc("/login", loginController)
}
//登录
func loginController(w http.ResponseWriter, r *http.Request) {
username := r.FormValue("username")
password := r.FormValue("password")
er := LoginService(username, password)
//把结构体转换为json数据
b, _ := json.Marshal(er)
//设置响应内容为json
w.Header().Set("Content-Type", "application/json;charset=utf-8")
w.Write(b)
}
- 在main.go中引用UserHandler()让user模块所有控制器生效
func main() {
s := http.Server{Addr: ":80"}
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
http.HandleFunc("/", welcome)
//用户相关
user.UserHandler()
s.ListenAndServe()
}
六.页面整体展示
- 确保%GOROOT%/src/github.com/下有gorilla文件夹,如果没有请进行配置(Github就可以)
- 由于在main中声明的全局对象无法被其他包调用,所以在commons文件夹下新建CommonVars.go,保证整个项目任何包都可以使这个对象进行设置Handler
package commons
import "github.com/gorilla/mux"
var (
Router = mux.NewRouter()
)
- 修改main.go中代码,称为restful风格
package main
import (
"net/http"
"html/template"
"user"
"commons"
"github.com/gorilla/mux"
)
//显示登录页面
func welcome(w http.ResponseWriter, r *http.Request) {
t, _ := template.ParseFiles("view/login.html")
t.Execute(w, nil)
}
//restfule风格显示页面
func showPage(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
t, _ := template.ParseFiles("view/" + vars["page"] + ".html")
t.Execute(w, nil)
}
func main() {
commons.Router.HandleFunc("/", welcome)
commons.Router.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
commons.Router.HandleFunc("/page/{page}", showPage)
//调用所有user模块的handler
user.UserHandler()
http.ListenAndServe(":80", commons.Router)
}
- 修改user/UserController中UserHandler()函数
//所有user模块的handler
func UserHandler() {
commons.Router.HandleFunc("/login", loginController)
}
七. 查询功能分析
- 从item.html页面中复制部分脚本
- datagrid请求的url为/showItem
- 填充的列属性中数据除了tb_item表以外,还有CategoryName是商品对应的类目名称,存在于tb_item_cat表中,所以在查询时是两表查询
$('#item_table').datagrid({
url: '/showItem',
columns: [[
{field: 'Id', title: '商品ID', width: 100},
{field: 'Title', title: '商品标题', width: 100},
{field: 'CategoryName', title: '叶子类目', width: 100},
{field: 'SellPoint', title: '卖点', width: 100},
{field: 'Price', title: '价格', width: 100},
{field: 'Num', title: '库存数量', width: 100},
{field: 'Barcode', title: '条形码', width: 100},
{field: 'Status', title: '状态', width: 100},
{field: 'Created', title: '创建日期', width: 100},
{field: 'Updated', title: '更新日期', width: 100}
]],
- EasyUI中Datagrid分页时要求返回数据格式为:(不是EgoResult了,否则无法正确显示)
{"rows":当前页数据,"total":总条数}
八.查询功能的代码实现
- 在commons文件夹下新建EasyUI.go
package commons
type Datagrid struct {
//当前页显示的数据
Rows interface{} `json:"rows"`
//总个数
Total int `json:"total"`
}
- 在src下新建文件夹item,并在item文件夹下新建TbItem.go
package item
//商品
type TbItem struct {
Id int
Title string
SellPoint string
Price int
Num int
Barcode string
Image string
Cid int
Status int8
Created string
Updated string
}
- 在item下新建TbItemDao.go实现数据访问,注意当数据库中有NULL值时的转换
package item
import (
"commons"
"fmt"
"database/sql"
)
/*
rows:每页显示的条数
page:当前第几页
*/
func selByPageDao(rows,page int) []TbItem{
//第一个表示:从哪条开始查询,0算起 第二个:查询几个
r,err:=commons.Dql("select * from tb_item limit ?,?",rows*(page-1),rows)
if err!=nil{
fmt.Println(err)
return nil
}
ts:=make([]TbItem,0)
for r.Next(){
var t TbItem
var s sql.NullString
//如果直接使用t.Barcode由于数据库中列为Null导致填充错误
r.Scan(&t.Id,&t.Title,&t.SellPoint,&t.Price,&t.Num,&s,&t.Image,&t.Cid,&t.Status,&t.Created,&t.Updated)
t.Barcode=s.String
ts=append(ts,t)
}
commons.CloseConn()
return ts
}
- 在item文件夹下新建TbItemService.go编写业务代码
- 目前不考虑总个数的问题
package item
import "commons"
func showItemService(page,rows int) (e *commons.Datagrid){
ts:=selByPageDao(rows,page)
if ts!=nil{
e= new(commons.Datagrid)
e.Rows=ts
return
}
return nil
}
- 在item文件夹下新建TbItemController.go编写控制器
package item
import (
"net/http"
"strconv"
"encoding/json"
"commons"
)
func ItemHandler(){
commons.Router.HandleFunc("/showItem",showItemController)
}
//显示商品信息
func showItemController(w http.ResponseWriter,r *http.Request){
page,_:=strconv.Atoi(r.FormValue("page"))
rows,_:=strconv.Atoi(r.FormValue("rows"))
datagrid:=showItemService(page,rows)
b,_:=json.Marshal(datagrid)
w.Header().Set("Content-Type","application/json;charset=utf-8")
w.Write(b)
}
- 修改main.go代码,添加item模块的引用
package main
import (
"net/http"
"html/template"
"user"
"commons"
"github.com/gorilla/mux"
"item"
)
//显示登录页面
func welcome(w http.ResponseWriter, r *http.Request) {
t, _ := template.ParseFiles("view/login.html")
t.Execute(w, nil)
}
//restful显示页面
func showPage(w http.ResponseWriter, r *http.Request){
vars:=mux.Vars(r)
t,_:=template.ParseFiles("view/"+vars["page"]+".html")
t.Execute(w,nil)
}
func main() {
commons.Router.PathPrefix("/static").Handler(http.StripPrefix("/static/",http.FileServer(http.Dir("static"))))
commons.Router.HandleFunc("/",welcome)
//满足/page/{page}格式的处理
commons.Router.HandleFunc("/page/{page}",showPage)
//用户
user.UserHandler()
//商品
item.ItemHandler()
http.ListenAndServe(":80",commons.Router)
}