最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

go mayfly開(kāi)源項(xiàng)目代碼結(jié)構(gòu)設(shè)計(jì)

 更新時(shí)間:2022年11月16日 11:43:08   作者:用戶6512516549724  
這篇文章主要為大家介紹了go mayfly開(kāi)源項(xiàng)目代碼結(jié)構(gòu)設(shè)計(jì)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

前言

今天繼續(xù)分享mayfly-go開(kāi)源代碼中代碼或者是包組織形式。猶豫之后這里不繪制傳統(tǒng)UML圖來(lái)描述,直接用代碼或許能更清晰。

開(kāi)源項(xiàng)目地址:github.com/may-fly/may…

開(kāi)源項(xiàng)目用到的,數(shù)據(jù)庫(kù)框架是gorm, web框架是 gin,下面是關(guān)于用戶(Account) 的相關(guān)設(shè)計(jì)和方法。

ModelBase 表結(jié)構(gòu)基礎(chǔ)類

項(xiàng)目基于gorm框架實(shí)現(xiàn)對(duì)數(shù)據(jù)庫(kù)操作。

pkg/model/model.go 是數(shù)據(jù)模型基礎(chǔ)類,里面封裝了數(shù)據(jù)庫(kù)應(yīng)包含的基本字段和基本操作方法,實(shí)際創(chuàng)建表應(yīng)該基于此結(jié)構(gòu)進(jìn)行繼承。

Model定義

對(duì)應(yīng)表結(jié)構(gòu)上的特點(diǎn)就是:所有表都包含如下字段。

type Model struct {
   Id         uint64     `json:"id"`            // 記錄唯一id
   CreateTime *time.Time `json:"createTime"`    // 關(guān)于創(chuàng)建者信息
   CreatorId  uint64     `json:"creatorId"`
   Creator    string     `json:"creator"`
   UpdateTime *time.Time `json:"updateTime"`    // 更新者信息
   ModifierId uint64     `json:"modifierId"`
   Modifier   string     `json:"modifier"`
}
// 將用戶信息傳入進(jìn)來(lái) 填充模型。 這點(diǎn)作者是根據(jù) m.Id===0 來(lái)判斷是 新增 或者 修改。 這種寫(xiě)
// 法有個(gè)問(wèn)題 必須 先用數(shù)據(jù)實(shí)例化再去調(diào)用此方法,順序不能反。。
func (m *Model) SetBaseInfo(account *LoginAccount)

數(shù)據(jù)操作基本方法

// 下面方法  不是作為model的方法進(jìn)行處理的。 方法都會(huì)用到 global.Db 也就是數(shù)據(jù)庫(kù)連接
// 將一組操作封裝到事務(wù)中進(jìn)行處理。  方法封裝很好。外部傳入對(duì)應(yīng)操作即可
func Tx(funcs ...func(db *gorm.DB) error) (err error) 
// 根據(jù)ID去表中查詢希望得到的列。若error不為nil則為不存在該記錄
func GetById(model interface{}, id uint64, cols ...string) error 
// 根據(jù)id列表查詢
func GetByIdIn(model interface{}, list interface{}, ids []uint64, orderBy ...string) 
// 根據(jù)id列查詢數(shù)據(jù)總量
func CountBy(model interface{}) int64 
// 根據(jù)id更新model,更新字段為model中不為空的值,即int類型不為0,ptr類型不為nil這類字段值
func UpdateById(model interface{}) error 
// 根據(jù)id刪除model
func DeleteById(model interface{}, id uint64) error 
// 根據(jù)條件刪除
func DeleteByCondition(model interface{}) 
// 插入model
func Insert(model interface{}) error 
// @param list為數(shù)組類型 如 var users *[]User,可指定為非model結(jié)構(gòu)體,即只包含需要返回的字段結(jié)構(gòu)體
func ListBy(model interface{}, list interface{}, cols ...string) 
// @param list為數(shù)組類型 如 var users *[]User,可指定為非model結(jié)構(gòu)體
func ListByOrder(model interface{}, list interface{}, order ...string) 
// 若 error不為nil,則為不存在該記錄
func GetBy(model interface{}, cols ...string) 
// 若 error不為nil,則為不存在該記錄
func GetByConditionTo(conditionModel interface{}, toModel interface{}) error 
// 根據(jù)條件 獲取分頁(yè)結(jié)果
func GetPage(pageParam *PageParam, conditionModel interface{}, toModels interface{}, orderBy ...string) *PageResult 
// 根據(jù)sql 獲取分頁(yè)對(duì)象
func GetPageBySql(sql string, param *PageParam, toModel interface{}, args ...interface{}) *PageResult 
// 通過(guò)sql獲得列表參數(shù)
func GetListBySql(sql string, params ...interface{}) []map[string]interface{}
// 通過(guò)sql獲得列表并且轉(zhuǎn)化為模型
func GetListBySql2Model(sql string, toEntity interface{}, params ...interface{}) error
  • 模型定義 表基礎(chǔ)字段,與基礎(chǔ)設(shè)置方法。
  • 定義了對(duì)模型操作基本方法。會(huì)使用全局的global.Db 數(shù)據(jù)庫(kù)連接。 數(shù)據(jù)庫(kù)最終操作收斂點(diǎn)。

Entity 表實(shí)體

文件路徑 internal/sys/domain/entity/account.go

Entity是繼承于 model.Model。對(duì)基礎(chǔ)字段進(jìn)行擴(kuò)展,進(jìn)而實(shí)現(xiàn)一個(gè)表設(shè)計(jì)。 例如我們用t_sys_account為例。

type Account struct {
   model.Model
   Username      string     `json:"username"`
   Password      string     `json:"-"`
   Status        int8       `json:"status"`
   LastLoginTime *time.Time `json:"lastLoginTime"`
   LastLoginIp   string     `json:"lastLoginIp"`
}
func (a *Account) TableName() string {
   return "t_sys_account"
}
// 是否可用
func (a *Account) IsEnable() bool {
   return a.Status == AccountEnableStatus
}

這樣我們就實(shí)現(xiàn)了 t_sys_account 表,在基礎(chǔ)模型上,完善了表獨(dú)有的方法。

相當(dāng)于在基礎(chǔ)表字段上 實(shí)現(xiàn)了 一個(gè)確定表的結(jié)構(gòu)和方法。

Repository 庫(kù)

文件路徑 internal/sys/domain/repository/account.go

主要定義 與** 此單表相關(guān)的具體操作的接口(與具體業(yè)務(wù)相關(guān)聯(lián)起來(lái)了)**

type Account interface {
   // 根據(jù)條件獲取賬號(hào)信息
   GetAccount(condition *entity.Account, cols ...string) error
   // 獲得列表
   GetPageList(condition *entity.Account, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
   // 插入
   Insert(account *entity.Account)
   //更新
   Update(account *entity.Account)
}

定義 賬號(hào)表操作相關(guān) 的基本接口,這里并沒(méi)有實(shí)現(xiàn)。 簡(jiǎn)單講將來(lái)我這個(gè)類至少要支持哪些方法。

Singleton

文件路徑 internal/sys/infrastructure/persistence/account_repo.go

是對(duì)Respository庫(kù)實(shí)例化,他是一個(gè)單例模式。

type accountRepoImpl struct{} // 對(duì)Resposity 接口實(shí)現(xiàn)
// 這里就很巧妙,用的是小寫(xiě)開(kāi)頭。 為什么呢??
func newAccountRepo() repository.Account {
   return new(accountRepoImpl)
}
// 方法具體實(shí)現(xiàn) 如下
func (a *accountRepoImpl) GetAccount(condition *entity.Account, cols ...string) error {
   return model.GetBy(condition, cols...)
}
func (m *accountRepoImpl) GetPageList(condition *entity.Account, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
}
func (m *accountRepoImpl) Insert(account *entity.Account) {
   biz.ErrIsNil(model.Insert(account), "新增賬號(hào)信息失敗")
}
func (m *accountRepoImpl) Update(account *entity.Account) {
   biz.ErrIsNil(model.UpdateById(account), "更新賬號(hào)信息失敗")
}

單例模式創(chuàng)建與使用

文件地址: internal/sys/infrastructure/persistence/persistence.go

// 項(xiàng)目初始化就會(huì)創(chuàng)建此變量
var accountRepo  = newAccountRepo()
// 通過(guò)get方法返回該實(shí)例
func GetAccountRepo() repository.Account { // 返回接口類型
   return accountRepo
}

定義了與Account相關(guān)的操作方法,并且以Singleton方式暴露給外部使用。

App 業(yè)務(wù)邏輯方法

文件地址:internal/sys/application/account_app.go

在業(yè)務(wù)邏輯方法中,作者已經(jīng)將接口 和 實(shí)現(xiàn)方法寫(xiě)在一個(gè)文件中了。

分開(kāi)確實(shí)太麻煩了。

定義業(yè)務(wù)邏輯方法接口

Account 業(yè)務(wù)邏輯模塊相關(guān)方法集合。

type Account interface {
   GetAccount(condition *entity.Account, cols ...string) error
   GetPageList(condition *entity.Account, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
   Create(account *entity.Account)
   Update(account *entity.Account)
   Delete(id uint64)
}

實(shí)現(xiàn)相關(guān)方法

// # 賬號(hào)模型實(shí)例化, 對(duì)應(yīng)賬號(hào)操作方法.  這里依然是 單例模式。
// 注意它入?yún)⑹?上面 repository.Account 類型
func newAccountApp(accountRepo repository.Account) Account {
   return &accountAppImpl{
      accountRepo: accountRepo,
   }
}
type accountAppImpl struct {
   accountRepo repository.Account
}
func (a *accountAppImpl) GetAccount(condition *entity.Account, cols ...string) error {}
func (a *accountAppImpl) GetPageList(condition *entity.Account, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {}
func (a *accountAppImpl) Create(account *entity.Account) {}
func (a *accountAppImpl) Update(account *entity.Account) {}
func (a *accountAppImpl) Delete(id uint64) {}

注意點(diǎn):

  • 入?yún)?repository.Account 是上面定義的基礎(chǔ)操作方法
  • 依然是Singleton 模式

被單例化實(shí)現(xiàn)

在文件 internal/sys/application/application.go 中定義全局變量。

定義如下:

// 這里將上面基本方法傳入進(jìn)去
var accountApp  = newAccountApp(persistence.GetAccountRepo()) 
func GetAccountApp() Account { // 返回上面定義的Account接口
   return accountApp
}

目前為止,我們得到了關(guān)于 Account 相關(guān)業(yè)務(wù)邏輯操作。

使用于gin路由【最外層】

例如具體登錄邏輯等。

文件路徑: internal/sys/api/account.go

type Account struct {
   AccountApp  application.Account
   ResourceApp application.Resource
   RoleApp     application.Role
   MsgApp      application.Msg
   ConfigApp   application.Config
}
// @router /accounts/login [post]
func (a *Account) Login(rc *ctx.ReqCtx) {
   loginForm := &form.LoginForm{}              // # 獲得表單數(shù)據(jù),并將數(shù)據(jù)賦值給特定值的
   ginx.BindJsonAndValid(rc.GinCtx, loginForm) // # 驗(yàn)證值類型
   // 判斷是否有開(kāi)啟登錄驗(yàn)證碼校驗(yàn)
   if a.ConfigApp.GetConfig(entity.ConfigKeyUseLoginCaptcha).BoolValue(true) { // # 從db中判斷是不是需要驗(yàn)證碼
      // 校驗(yàn)驗(yàn)證碼
      biz.IsTrue(captcha.Verify(loginForm.Cid, loginForm.Captcha), "驗(yàn)證碼錯(cuò)誤") // # 用的Cid(密鑰生成id 和 驗(yàn)證碼去驗(yàn)證)
   }
   // # 用于解密獲得原始密碼,這種加密方法對(duì)后端庫(kù)來(lái)說(shuō),也是不可見(jiàn)的
   originPwd, err := utils.DefaultRsaDecrypt(loginForm.Password, true)
   biz.ErrIsNilAppendErr(err, "解密密碼錯(cuò)誤: %s")
   // # 定義一個(gè)用戶實(shí)體
   account := &entity.Account{Username: loginForm.Username}
   err = a.AccountApp.GetAccount(account, "Id", "Username", "Password", "Status", "LastLoginTime", "LastLoginIp")
   biz.ErrIsNil(err, "用戶名或密碼錯(cuò)誤(查詢錯(cuò)誤)")
   fmt.Printf("originPwd is: %v, %v\n", originPwd, account.Password)
   biz.IsTrue(utils.CheckPwdHash(originPwd, account.Password), "用戶名或密碼錯(cuò)誤")
   biz.IsTrue(account.IsEnable(), "該賬號(hào)不可用")
   // 校驗(yàn)密碼強(qiáng)度是否符合
   biz.IsTrueBy(CheckPasswordLever(originPwd), biz.NewBizErrCode(401, "您的密碼安全等級(jí)較低,請(qǐng)修改后重新登錄"))
   var resources vo.AccountResourceVOList
   // 獲取賬號(hào)菜單資源
   a.ResourceApp.GetAccountResources(account.Id, &resources)
   // 菜單樹(shù)與權(quán)限code數(shù)組
   var menus vo.AccountResourceVOList
   var permissions []string
   for _, v := range resources {
      if v.Type == entity.ResourceTypeMenu {
         menus = append(menus, v)
      } else {
         permissions = append(permissions, *v.Code)
      }
   }
   // 保存該賬號(hào)的權(quán)限codes
   ctx.SavePermissionCodes(account.Id, permissions)
   clientIp := rc.GinCtx.ClientIP()
   // 保存登錄消息
   go a.saveLogin(account, clientIp)
   rc.ReqParam = fmt.Sprintln("登錄ip: ", clientIp)
   // 賦值loginAccount 主要用于記錄操作日志,因?yàn)椴僮魅罩颈4嬲?qǐng)求上下文沒(méi)有該信息不保存日志
   rc.LoginAccount = &model.LoginAccount{Id: account.Id, Username: account.Username}
   rc.ResData = map[string]interface{}{
      "token":         ctx.CreateToken(account.Id, account.Username),
      "username":      account.Username,
      "lastLoginTime": account.LastLoginTime,
      "lastLoginIp":   account.LastLoginIp,
      "menus":         menus.ToTrees(0),
      "permissions":   permissions,
   }
}

可以看出來(lái),一個(gè)業(yè)務(wù)是由多個(gè)App組合起來(lái)共同來(lái)完成的。

具體使用的時(shí)候在router初始化時(shí)。

account := router.Group("sys/accounts")
a := &api.Account{
   AccountApp:  application.GetAccountApp(),
   ResourceApp: application.GetResourceApp(),
   RoleApp:     application.GetRoleApp(),
   MsgApp:      application.GetMsgApp(),
   ConfigApp:   application.GetConfigApp(),
}
// 綁定單例模式
account.POST("login", func(g *gin.Context) {
   ctx.NewReqCtxWithGin(g).
      WithNeedToken(false).
      WithLog(loginLog). // # 將日志掛到請(qǐng)求對(duì)象中
      Handle(a.Login)   // 對(duì)應(yīng)處理方法
})

總概覽圖

下圖描述了,從底層模型到上層調(diào)用的依賴關(guān)系鏈。

問(wèn)題來(lái)了: 實(shí)際開(kāi)發(fā)中,應(yīng)該怎么區(qū)分。

  • 屬于模型的基礎(chǔ)方法
  • 數(shù)據(jù)模型操作上的方法
  • 與單獨(dú)模型相關(guān)的操作集
  • 與應(yīng)用相關(guān)的方法集

區(qū)分開(kāi)他們才能知道代碼位置寫(xiě)在哪里。

以上就是go mayfly開(kāi)源項(xiàng)目代碼結(jié)構(gòu)設(shè)計(jì)的詳細(xì)內(nèi)容,更多關(guān)于go mayfly開(kāi)源代碼結(jié)構(gòu)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 解決golang http.FileServer 遇到的坑

    解決golang http.FileServer 遇到的坑

    這篇文章主要介紹了解決golang http.FileServer 遇到的坑,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-12-12
  • GoFrame?glist?基礎(chǔ)使用和自定義遍歷

    GoFrame?glist?基礎(chǔ)使用和自定義遍歷

    這篇文章主要為大家介紹了GoFrame?glist的基礎(chǔ)使用和自定義遍歷示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • golang實(shí)踐-第三方包為私有庫(kù)的配置方案

    golang實(shí)踐-第三方包為私有庫(kù)的配置方案

    這篇文章主要介紹了golang實(shí)踐-第三方包為私有庫(kù)的配置方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-05-05
  • 淺析Go項(xiàng)目中的依賴包管理與Go?Module常規(guī)操作

    淺析Go項(xiàng)目中的依賴包管理與Go?Module常規(guī)操作

    這篇文章主要為大家詳細(xì)介紹了Go項(xiàng)目中的依賴包管理與Go?Module常規(guī)操作,文中的示例代碼講解詳細(xì),對(duì)我們深入了解Go語(yǔ)言有一定的幫助,需要的可以跟隨小編一起學(xué)習(xí)一下
    2023-10-10
  • Go到底能不能實(shí)現(xiàn)安全的雙檢鎖(推薦)

    Go到底能不能實(shí)現(xiàn)安全的雙檢鎖(推薦)

    這篇文章主要介紹了Go到底能不能實(shí)現(xiàn)安全的雙檢鎖,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-05-05
  • Go基礎(chǔ)教程之環(huán)境搭建及常用命令

    Go基礎(chǔ)教程之環(huán)境搭建及常用命令

    這篇文章主要介紹了Go基礎(chǔ)教程之環(huán)境搭建及常用命令的相關(guān)資料,包括Go語(yǔ)言簡(jiǎn)介、環(huán)境配置、包管理工具GoModules以及常用命令的全面介紹,需要的朋友可以參考下
    2025-03-03
  • Go語(yǔ)言操作etcd的示例詳解

    Go語(yǔ)言操作etcd的示例詳解

    etcd是使用Go語(yǔ)言開(kāi)發(fā)的一個(gè)開(kāi)源的、高可用的分布式key—value存儲(chǔ)系統(tǒng),可以用于配置共享和服務(wù)的注冊(cè)和發(fā)現(xiàn),下面我們就來(lái)看看Go語(yǔ)言是如何操作etcd的吧
    2024-03-03
  • 基于Golang設(shè)計(jì)一套可控的定時(shí)任務(wù)系統(tǒng)

    基于Golang設(shè)計(jì)一套可控的定時(shí)任務(wù)系統(tǒng)

    這篇文章主要為大家學(xué)習(xí)介紹了如何基于Golang設(shè)計(jì)一套可控的定時(shí)任務(wù)系統(tǒng),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-07-07
  • 一文帶你熟悉Go語(yǔ)言中函數(shù)的使用

    一文帶你熟悉Go語(yǔ)言中函數(shù)的使用

    這篇文章主要和大家分享一下Go語(yǔ)言中的函數(shù)的使用,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Go語(yǔ)言有一定的幫助,需要的小伙伴可以參考一下
    2022-11-11
  • Go開(kāi)發(fā)go-optioner工具實(shí)現(xiàn)輕松生成函數(shù)選項(xiàng)模式代碼

    Go開(kāi)發(fā)go-optioner工具實(shí)現(xiàn)輕松生成函數(shù)選項(xiàng)模式代碼

    go-optioner?是一個(gè)在?Go?代碼中生成函數(shù)選項(xiàng)模式代碼的工具,可以根據(jù)給定的結(jié)構(gòu)定義自動(dòng)生成相應(yīng)的選項(xiàng)代碼,下面就來(lái)聊聊go-optioner是如何使用的吧
    2023-07-07

最新評(píng)論

元江| 武宁县| 古蔺县| 桦川县| 腾冲县| 景德镇市| 万山特区| 洛宁县| 衡南县| 信宜市| 宁波市| 同心县| 南城县| 漳浦县| 西昌市| 襄垣县| 玉山县| 临高县| 收藏| 盐亭县| 宁都县| 福贡县| 行唐县| 虞城县| 特克斯县| 隆昌县| 通江县| 克山县| 连州市| 筠连县| 宣汉县| 颍上县| 虞城县| 南郑县| 茂名市| 北辰区| 锦屏县| 河北省| 长垣县| 濉溪县| 阳高县|