Go語言實現excel匯入無限級選單結構

雪山飛豬發表於2020-11-24

一、需求

最近有一個需求,要實現一個無限級結構的樹型選單,差不多長下面這個樣子

我們知道無限級實現思路都是用一個parent_id將各種層級串聯起來,頂級的parent_id為0,例如如下層級的選單

選單一
    選單二
        選單三
    選單四
        選單五
            選單六
            選單7
                  選單八

在資料庫中的儲存一般是如下形式

原理就會記錄每一個選單的父級ID(parent_id),通過這樣的父級ID會構造出一棵樹型結構,層級(level)是為了標明當前選單是處於哪個層級

問題來了,一般這樣的結果要是一條一條插入,再人工用parent_id串起來,太反人類了,低效!

產品會讓工程師通過表格匯入這樣的資料,表格差不多都長如下這個樣子

我們需要用過程式碼來實現生成上面資料庫的結果,talk is cheap, show you the code

二、程式碼實現

func ImportMenus() (res interface{}, err error) {
    columns := 5                                              //支援的無限級選單數量,想支援多少級寫多少
    templateFile := "/Users/chenqionghe/Downloads/menus.xlsx" //匯入的表格路徑
    f, err := excelize.OpenFile(templateFile)                 //讀取表格
    if err != nil {
        return nil, err
    }
    rows, err := f.GetRows("Sheet1")
    if err != nil {
        return nil, err
    }
    var allRowIds = make([][]int, len(rows)) //初始化儲存ID的陣列,通過下標定位對應選單生成的ID
    for i, _ := range rows {
        allRowIds[i] = make([]int, columns)
    }
    var parentId int
    tx := db.DB().Begin()
    exception.Block{
        Try: func() {
            for i, row := range rows {
                if i == 0 { //表頭跳過
                    continue
                }
                //構造無限級選單
                for j := 0; j < columns; j++ {
                    if row[j] == "" { //空值不操作
                        continue
                    }
                    if j == 0 && row[j] != "" { //頂級按鈕父級ID是0
                        parentId = 0
                    }
                    if j > 0 { //非頂級,向前或向上尋找最近的父級ID
                        if allRowIds[i][j-1] != 0 {
                            parentId = allRowIds[i][j-1] //向前找ID作為父級ID
                        } else {
                            for z := i - 1; z > 0; z-- {
                                if allRowIds[z][j-1] != 0 {
                                    parentId = allRowIds[z][j-1] //向上找ID作為父級ID
                                    break
                                }
                            }
                        }
                    }
                    newData := &model.Menu{Name: row[j], ParentID: parentId, Level: j + 1}
                    if err = tx.Save(newData).Error; err != nil { //選單插入資料庫
                        panic(err)
                    }

                    allRowIds[i][j] = newData.ID //儲存當ID到陣列對應陣列下標中,供後續選單作為父級ID使用
                }
            }
            tx.Commit()
            err = nil
        },
        Catch: func(e interface{}) {
            tx.Rollback()
            err = fmt.Errorf("err: %v", e)
        },
    }.Do()
    return allRowIds, err
}

三、程式碼測試

先建立對應的資料表,結構如下

CREATE TABLE `menu` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '選單ID',
  `name` varchar(255) NOT NULL COMMENT '選單名稱',
  `parent_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '上級選單ID',
  `level` tinyint(1) NOT NULL COMMENT '層級',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8mb4 COMMENT='選單表';

簡單例子

我們來測試一下匯入上面的表格

執行結果如下

可以看到,結果和我們設想的資料庫結果完全一樣!

複雜例子

好,我們再測試一個更復雜的例子,表格模板如下

匯入的結果如下

這樣就用Go實現了一個支援無限級選單的表格匯入,以上程式碼由chenqionghe提供,轉載請標明出處,giao~

相關文章