從零開始Gin Web+Vue商城的搭建(四)-- 重構(gòu)用戶模塊和框架設(shè)計

我覺得差不多該更新了冤荆,挖個坑明天寫辽话,十一之前BUG都清完了题篷,也沒什么事了。

這一章按理來說應(yīng)該是“商城首頁信息和購物車模塊”厅目,但是入職新公司以后我看到了好多高端操作番枚,所以順道重寫一下之前亂七八糟的框架法严。

而且golang的web項目相對于那些重型的高封裝語言比如.net MVC項目來說要好很多,用起來很自由葫笼。前后分離深啤,只要支持REST就可以了,并不需要前端再去格式化model路星,雖然也有好處但總體感覺用起來比較笨重溯街。

然后想起上一章好像還坑在session那呢,我晚上回去好好看一下洋丐。


今天來填坑

看一下我們之前慘的不行的main方法

func main() {
    router := gin.Default()
    RouterInit(router)
    Helper.Dbinit()
    router.Run(":8081")
}

雖然功能是實現(xiàn)了呈昔,但是及其簡易。不光連接字符串是寫死的友绝,一些初始化函數(shù)也全都沒有封裝堤尾。一個靈活的網(wǎng)站需要很多配置文件,還要相對應(yīng)的log跟蹤及錯誤處理迁客,我們改一下main文件讓啟動函數(shù)更豐富郭宝。

func main() {
    //根據(jù)shell文件去啟動編譯好的go文件
    //shell命令:./ginmall -c web-config.toml

    //定義flag參數(shù),返回一個相應(yīng)的指針
    cfgFlag := flag.String("c", "", "配置文件路徑")
    //調(diào)用flag.Parse()解析命令行參數(shù)到定義的flag
    flag.Parse()

    //解析配置文件
    params := config.NewConfiguration()

    if len(*cfgFlag) > 0 { //cfgFlag是*string型,取地址符
        //
        params.InitFromFile(*cfgFlag)
    } else {
        fmt.Println("配置文件錯誤掷漱,請檢查內(nèi)容格式")
    }

    //設(shè)置time包時區(qū)(time包是個很神奇的東西粘室,默認(rèn)采用UTC時區(qū),最好設(shè)置一下)
    //Asia包里沒有北京時區(qū)卜范,可以寫成Asia/Shanghai衔统,小伙子們注意一下。
    //具體的時區(qū)命名可以解壓$GOROOT/lib/time/zoneinfo.zip 這個文件打開查看海雪。
    location, err := time.LoadLocation("Local")
    if err != nil {
        fmt.Println("本地時區(qū)初始化失敗! " + err.Error())
        os.Exit(1)
    }
    fmt.Println("本地時區(qū)初始化成功!")

    //初始化logger
    logger := logger.NewLogger()
    fmt.Println("日志初始化成功!")

    //初始化服務(wù)
    server := "server"
    if server == nil {
        logger.Error("服務(wù)初始化失敗")
        os.Exit(1)
    }

    //啟動服務(wù)
    server.Start()

    // router := gin.Default()
    // RouterInit(router)
    // Helper.Dbinit()
    // router.Run(":8081")
}

用到的三個包缰冤,config,logger喳魏,server

//config.go
package config

import (
    "fmt"

    "github.com/BurntSushi/toml"
)

type Configuration struct {
    Port string `toml:"port"` // 服務(wù)器監(jiān)聽端口

    // 數(shù)據(jù)庫相關(guān)參數(shù)
    DbUrl    string `toml:"database_url"`    // 數(shù)據(jù)庫URL
    DbPort   string `toml:"database_port"`   // 數(shù)據(jù)庫端口
    DbName   string `toml:"database_name"`   // 數(shù)據(jù)庫名稱
    DbUser   string `toml:"database_user"`   // 數(shù)據(jù)庫用戶名
    DbPasswd string `toml:"database_passwd"` // 數(shù)據(jù)庫密碼

    // Redis數(shù)據(jù)庫相關(guān)參數(shù)
    RedisUrl    string `toml:"redis_url"`    // 數(shù)據(jù)庫URL
    RedisPort   string `toml:"redis_port"`   // 數(shù)據(jù)庫端口
    RedisPasswd string `toml:"redis_passwd"` // 數(shù)據(jù)庫密碼

    // 日志相關(guān)參數(shù)
    LogLevel string `toml:"log_level"`
    LogDest  string `toml:"log_dest"`
    LogDir   string `toml:"log_dir"`
}

func NewConfiguration() *Configuration {

    return &Configuration{ // 在此提供默認(rèn)值(為所有參數(shù)提供默認(rèn)值)
        Port: "8081",

        LogDir:   "./log",
        LogLevel: "debug",
        LogDest:  "file",

        DbUrl:    "localhost",
        DbPort:   "3306",
        DbName:   "sa",
        DbUser:   "root",
        DbPasswd: "Your PassWord",

        RedisUrl:  "127.0.0.1",
        RedisPort: "6379",
    }

}
func (this *Configuration) InitFromFile(path string) { // 用于啟動時加載配置文件

    if _, err := toml.DecodeFile(path, this); err != nil {
        panic(fmt.Sprintf("can't decode conf file: [%s]", path))
    }

}
//logger.go
package logger

import (
    "fmt"

    "github.com/sirupsen/logrus"
)

type Logger struct {
    logrus *logrus.Entry
}

//logger方法我沒具體寫棉浸,有興趣的朋友按需實現(xiàn)吧
func NewLogger() *Logger {
    logger := &Logger{
        logrus: logrus.WithFields(logrus.Fields{}),
    }
    return logger
}

func (logger *Logger) Info(str error) {
    fmt.Print("Info:" + str.Error())
}

func (logger *Logger) Warn(str error) {
    fmt.Print("Warn:" + str.Error())
}

func (logger *Logger) Error(str error) {
    fmt.Print("Error:" + str.Error())
}

//service.go
package service

import (
    "ginMall/logger"
    "time"

    "ginMall/config"
    "ginMall/dao"
    "ginMall/httpsvr"
)

type Service struct {
    cfgFlag string // 配置文件路徑

    config   *config.Configuration //系統(tǒng)配置
    logger   *logger.Logger        //日志
    location *time.Location        // 時區(qū)信息
    httpsvr  *httpsvr.HttpServer

    daomgr *dao.DaoManager //引用Dao層

    //redismgr *redis.RedisManager //Redis
}

func NewService(params *config.Configuration, logger *logger.Logger, location *time.Location, cfg string) *Service {

    // 初始化數(shù)據(jù)庫連接
    dsn := params.DbUser + ":" + params.DbPasswd + "@tcp" + "(" + params.DbUrl + ":" + params.DbPort + ")" + "/" + params.DbName + "?charset=utf8"

    // 初始化Dao層
    daomgr, err := dao.NewDaoManager(dsn)
    if err != nil {
        logger.Error(err)
    }

    //第一章寫的router,挪到httpserver里
    httpsvr := httpsvr.NewHttpServer()

    service := &Service{

        cfgFlag:  cfg,
        config:   params,
        logger:   logger,
        location: location,
        daomgr:   daomgr,
        httpsvr:  httpsvr,
    }
    return service
}

func (this *Service) Start() {
    go this.httpsvr.Start()
}

展開說一下這個service層吧刺彩,看到我還注釋掉了Redis服務(wù)迷郑。其實你可以在service層里實例化多個DB連接,小項目也沒有什么必要非得寫工廠類创倔,需要用哪個就調(diào)用嗡害,我覺得這個是自己手搭框架最舒服的地方。

然后看一下Dao層畦攘,我只是在Dao層初步封裝了一下orm框架霸妹,在調(diào)用的時候可以直接通過GetEngine方法獲取orm的實例。而且gorm框架底層支持手寫sql知押,我研究的不多叹螟,喜歡學(xué)習(xí)的朋友可以點進去看一下源碼鹃骂。功能比較全。

然后這個orm框架罢绽,我不知道別的公司或者國內(nèi)大趨勢是什么樣子的畏线,用的多不多、好不好用我都不太清楚良价,我只是撿起手頭的東西拿來試試而已寝殴。不過這些都是小事情,代碼已經(jīng)貼出來了自己實例化一下就好了明垢。

//dao.go
package dao

import (
    "ginMall/Model"

    "github.com/jinzhu/gorm"
)

type DaoManager struct {
    DB *gorm.DB
    //logger *logger.Logger
}

func NewDaoManager(constr string) (*DaoManager, error) {

    db, err := gorm.Open("mysql", constr)
    if err != nil {
        return nil, err
    }

    //SetMaxOpenConns用于設(shè)置最大打開的連接數(shù)
    //SetMaxIdleConns用于設(shè)置閑置的連接數(shù)
    db.DB().SetMaxIdleConns(10)
    db.DB().SetMaxOpenConns(100)

    // 啟用Logger蚣常,顯示詳細(xì)日志
    db.LogMode(true)

    // ORM自動遷移模式
    db.AutoMigrate(&Model.UserModel{},
        &Model.UserDetailModel{},
        &Model.UserAuthsModel{},
    )

    dao := &DaoManager{
        DB: db,
        //logger: logger,
    }
    return dao, nil
}
func (this *DaoManager) GetEngine() *gorm.DB {
    return this.DB
}

然后service層里還寫了一個httpserver,這個主要還是業(yè)務(wù)層(我架構(gòu)學(xué)的很爛痊银,說錯了請指出)抵蚊。實現(xiàn)系統(tǒng)的緩存及路由功能。
我在之前章節(jié)也講了一下session曼验,當(dāng)時主要是用的插件,后來其實經(jīng)過一些項目的實戰(zhàn)以后(尤其是從.net脫坑以后)發(fā)現(xiàn)其實之前腦子很僵化粘姜。主要是.net的session就是直接在httpcontext里寫好了了鬓照,用起來很無腦,而且還挺好使的孤紧,所以也沒有很在意豺裆。
等真的到golang的實戰(zhàn)時候,去問同事“我怎么把一個常用表數(shù)據(jù)存到session里”的時候号显,同事告訴我你可以直接寫全局變量臭猜。然后我瞬間就懵掉了,甚至不知道全局變量到底怎么寫押蚤。翻了半天代碼蔑歌,他們把所有用到的表變量,包括用戶信息什么的在初始化的時候就已經(jīng)寫好了揽碘。
但是這樣也有局限性次屠,也就是現(xiàn)在公司很小,只是單臺服務(wù)器雳刺,等需要搭建集群的時候就會出現(xiàn)內(nèi)存不同步的問題劫灶,所以我推薦大家還是寫到redis里。
等之后一兩個章節(jié)我會把這個代碼繼續(xù)完善掖桦,加一個redis的服務(wù)本昏。我之前也寫過一章redis存kv的簡單思想,到十一在家沒事干的時候補一篇文章枪汪,寫一些用戶權(quán)限及大量表關(guān)聯(lián)的這些解決方法和思想

//httpsvr.go
package httpsvr

import (
    "ginMall/FPList"
    "ginMall/session"
    "net/http"

    "github.com/gin-contrib/cors"
    "github.com/gin-gonic/gin"
)

type HttpServer struct {
    gin     *gin.Engine
    session *session.Session
}

//HTTPserver包含幾大部分功能
//1:路由
//2:保存session涌穆,因為session本質(zhì)上也是存儲在內(nèi)存之中怔昨,golang也并沒有原生支持session,所以可以直接將所有信息直接保存在系統(tǒng)里
//  也可以存儲靜態(tài)表蒲犬,也可以存儲用戶信息
//3:保存service層及其他信息
func NewHttpServer() *HttpServer {

    session := session.NewSession()

    svr := &HttpServer{
        gin:     gin.Default(),
        session: session,
    }
    return svr
}

func (this *HttpServer) Start() {

    this.gin.Use(cors.Default())

    this.gin.GET("/", func(c *gin.Context) {
        c.String(http.StatusOK, "It works On 8081")
    })
    this.gin.GET("/Login", FPList.GetList)
    this.gin.GET("/FPList", FPList.GetList)
}

上面這部分代碼其實也就是把之前寫在router里的邏輯粘過來罷了朱监。
session類我沒有仔細(xì)寫,以后要被redis替換掉原叮,只是簡單寫了get和set方法赫编。
需要注意的一點就是用session的時候,變量接收interface需要斷言奋隶,用反射也好事先用之前寫過的fmt.Sprintf("%T",)也好擂送,都可以實現(xiàn)。

啊唯欣,晚上看了篇文章嘹吨,然后發(fā)現(xiàn)自己理解錯了,這東西不能叫session境氢,只能叫全局變量蟀拷,等過兩天仔細(xì)研究明白了再寫這部分。

package session

type Session struct {
    UserInfo         map[string]string
    SomeThingYouNeed map[string]interface{}
}

func NewSession() *Session {
    session := &Session{
        UserInfo:         make(map[string]string),
        SomeThingYouNeed: make(map[string]interface{}),
    }
    return session
}

func (this *Session) Set(key string, value interface{}) {
    this.SomeThingYouNeed[key] = value
}

//Get方法返回interface是需要斷言的(我記得我在前面一兩章說過用法)
//或者你也在Set里直接存Json然后用utils.GetJsonStruct(object)
//寫法隨便你萍聊,按你的需求來定
func (this *Session) Get(key string) interface{} {

    if _, ok := this.SomeThingYouNeed[key]; ok {

        return this.SomeThingYouNeed[key]

    } else {
        return nil
    }
}

我一個下午摸了半天魚问芬,也就寫了這么多東西∈俳埃看看明天有沒有BUG此衅,沒有的話就繼續(xù)補一下redis,然后補一下git和Jenkins的用法亭螟,因為之后公司內(nèi)部晉升答辯的時候會用到挡鞍。
代碼已經(jīng)全都push到我的git上面了,有興趣的同學(xué)可以直接clone一份预烙,如果我寫的有什么含糊不清的地方墨微,或者有什么問題也歡迎指出。
git:https://github.com/nds15763/ginMall

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末扁掸,一起剝皮案震驚了整個濱河市欢嘿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌也糊,老刑警劉巖炼蹦,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異狸剃,居然都是意外死亡掐隐,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來虑省,“玉大人匿刮,你說我怎么就攤上這事√骄保” “怎么了熟丸?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長伪节。 經(jīng)常有香客問我光羞,道長,這世上最難降的妖魔是什么怀大? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任纱兑,我火速辦了婚禮,結(jié)果婚禮上化借,老公的妹妹穿的比我還像新娘潜慎。我一直安慰自己,他們只是感情好蓖康,可當(dāng)我...
    茶點故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布铐炫。 她就那樣靜靜地躺著,像睡著了一般蒜焊。 火紅的嫁衣襯著肌膚如雪倒信。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天山涡,我揣著相機與錄音堤结,去河邊找鬼唆迁。 笑死鸭丛,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的唐责。 我是一名探鬼主播鳞溉,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼鼠哥!你這毒婦竟也來了熟菲?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤朴恳,失蹤者是張志新(化名)和其女友劉穎抄罕,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體于颖,經(jīng)...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡呆贿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片做入。...
    茶點故事閱讀 38,650評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡冒晰,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出竟块,到底是詐尸還是另有隱情壶运,我是刑警寧澤,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布浪秘,位于F島的核電站蒋情,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏秫逝。R本人自食惡果不足惜恕出,卻給世界環(huán)境...
    茶點故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望违帆。 院中可真熱鬧浙巫,春花似錦、人聲如沸刷后。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽尝胆。三九已至丧裁,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間含衔,已是汗流浹背煎娇。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留贪染,地道東北人缓呛。 一個月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像杭隙,于是被迫代替她去往敵國和親哟绊。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,527評論 2 349