欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

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

前言

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

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

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

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

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

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

Model定義

對應(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)來 填充模型。 這點(diǎn)作者是根據(jù) m.Id===0 來判斷是 新增 或者 修改。 這種寫
// 法有個(gè)問題 必須 先用數(shù)據(jù)實(shí)例化再去調(diào)用此方法,順序不能反。。
func (m *Model) SetBaseInfo(account *LoginAccount)

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

// 下面方法  不是作為model的方法進(jìn)行處理的。 方法都會(huì)用到 global.Db 也就是數(shù)據(jù)庫連接
// 將一組操作封裝到事務(wù)中進(jìn)行處理。  方法封裝很好。外部傳入對應(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ù)條件 獲取分頁結(jié)果
func GetPage(pageParam *PageParam, conditionModel interface{}, toModels interface{}, orderBy ...string) *PageResult 
// 根據(jù)sql 獲取分頁對象
func GetPageBySql(sql string, param *PageParam, toModel interface{}, args ...interface{}) *PageResult 
// 通過sql獲得列表參數(shù)
func GetListBySql(sql string, params ...interface{}) []map[string]interface{}
// 通過sql獲得列表并且轉(zhuǎn)化為模型
func GetListBySql2Model(sql string, toEntity interface{}, params ...interface{}) error
  • 模型定義 表基礎(chǔ)字段,與基礎(chǔ)設(shè)置方法。
  • 定義了對模型操作基本方法。會(huì)使用全局的global.Db 數(shù)據(jù)庫連接。 數(shù)據(jù)庫最終操作收斂點(diǎn)。

Entity 表實(shí)體

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

Entity是繼承于 model.Model。對基礎(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 庫

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

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

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) 的基本接口,這里并沒有實(shí)現(xiàn)。 簡單講將來我這個(gè)類至少要支持哪些方法。

Singleton

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

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

type accountRepoImpl struct{} // 對Resposity 接口實(shí)現(xiàn)
// 這里就很巧妙,用的是小寫開頭。 為什么呢??
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()
// 通過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)方法寫在一個(gè)文件中了。

分開確實(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í)例化, 對應(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)證值類型
   // 判斷是否有開啟登錄驗(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)證)
   }
   // # 用于解密獲得原始密碼,這種加密方法對后端庫來說,也是不可見的
   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, "您的密碼安全等級較低,請修改后重新登錄"))
   var resources vo.AccountResourceVOList
   // 獲取賬號(hào)菜單資源
   a.ResourceApp.GetAccountResources(account.Id, &resources)
   // 菜單樹與權(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嬲埱笊舷挛臎]有該信息不保存日志
   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,
   }
}

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

具體使用的時(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). // # 將日志掛到請求對象中
      Handle(a.Login)   // 對應(yīng)處理方法
})

總概覽圖

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

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

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

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

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

相關(guān)文章

  • golang int64轉(zhuǎn)int的方法

    golang int64轉(zhuǎn)int的方法

    這篇文章主要介紹了golang int64轉(zhuǎn)int,本文給大家提供兩種方法 ,將 golang int64 轉(zhuǎn)換為golang int,結(jié)合實(shí)例代碼給大家分享轉(zhuǎn)換方法,需要的朋友可以參考下
    2023-01-01
  • Gin golang web開發(fā)模型綁定實(shí)現(xiàn)過程解析

    Gin golang web開發(fā)模型綁定實(shí)現(xiàn)過程解析

    這篇文章主要介紹了Gin golang web開發(fā)模型綁定實(shí)現(xiàn)過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-10-10
  • Go語言map元素的刪除和清空

    Go語言map元素的刪除和清空

    本文主要介紹了Go語言map元素的刪除和清空,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04
  • 教你一分鐘配置好Go語言開發(fā)環(huán)境(多種操作系統(tǒng))

    教你一分鐘配置好Go語言開發(fā)環(huán)境(多種操作系統(tǒng))

    在這篇文章中,我們從頭到尾一步步指導(dǎo)你配置Golang開發(fā)環(huán)境,并編寫你的第一個(gè)"Hello,?World!"程序,我們詳細(xì)解釋了在多種操作系統(tǒng)(包括Windows、Linux和macOS)下的安裝過程、環(huán)境變量設(shè)置以及如何驗(yàn)證安裝是否成功
    2023-09-09
  • Go語言對前端領(lǐng)域的入侵WebAssembly運(yùn)行原理

    Go語言對前端領(lǐng)域的入侵WebAssembly運(yùn)行原理

    這篇文章主要為大家介紹了不安分的Go語言對Web?前端領(lǐng)域的入侵WebAssembly運(yùn)行原理實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-07-07
  • Go語言實(shí)現(xiàn)的樹形結(jié)構(gòu)數(shù)據(jù)比較算法實(shí)例

    Go語言實(shí)現(xiàn)的樹形結(jié)構(gòu)數(shù)據(jù)比較算法實(shí)例

    這篇文章主要介紹了Go語言實(shí)現(xiàn)的樹形結(jié)構(gòu)數(shù)據(jù)比較算法,實(shí)例分析了樹形結(jié)構(gòu)數(shù)據(jù)比較算法的實(shí)現(xiàn)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-02-02
  • golang中字符串MD5生成方式總結(jié)

    golang中字符串MD5生成方式總結(jié)

    在本篇文章里小編給大家整理的是一篇關(guān)于golang中字符串MD5生成方式總結(jié)內(nèi)容,有興趣的朋友們可以跟著學(xué)習(xí)參考下。
    2021-07-07
  • 使用Lumberjack+zap進(jìn)行日志切割歸檔操作

    使用Lumberjack+zap進(jìn)行日志切割歸檔操作

    這篇文章主要介紹了使用Lumberjack+zap進(jìn)行日志切割歸檔操作,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • 詳解Golang函數(shù)式選項(xiàng)(Functional?Options)模式

    詳解Golang函數(shù)式選項(xiàng)(Functional?Options)模式

    什么是函數(shù)式選項(xiàng)模式,為什么要這么寫,這個(gè)編程模式解決了什么問題呢?其實(shí)就是為了解決動(dòng)態(tài)靈活的配置不同的參數(shù)的問題。下面通過本文給大家介紹Golang函數(shù)式選項(xiàng)(Functional?Options)模式的問題,感興趣的朋友一起看看吧
    2021-12-12
  • golang網(wǎng)絡(luò)通信超時(shí)設(shè)置方式

    golang網(wǎng)絡(luò)通信超時(shí)設(shè)置方式

    這篇文章主要介紹了golang網(wǎng)絡(luò)通信超時(shí)設(shè)置方式,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12

最新評論