package main import ( "database/sql" "encoding/json" "fmt" "github.com/360EntSecGroup-Skylar/excelize" _ "github.com/go-sql-driver/mysql" "strconv" "strings" "time" ) const sheetName = `Sheet1` const fileName = `公务员系统数据-` const fileType = `.xlsx` const sqlCodeStr = `SELECT concat(code_type,code_value),code_name FROM CODE_VALUE where code_type in('%s') union SELECT concat(code_type,code_value),code_name3 FROM CODE_VALUE where code_type='ZB01'` const sqlB01 = `select b0111,b0121,b0101 from b01 where b0111<>'-1' order by b0121,sortid` const sqlA01A0279 = `select %s from a01 where a0163='1' and exists(select 1 from a02 a02 where a01.a0000=a02.a0000 and a0279='1' and A0201B='%s') order by (select lpad(max(a0225),5,'0') from a02 where a01.a0000=a02.a0000 and a0279='1' and A0201B='%s')` const sqlA01A0281 = `select %s from a01 where a0163='1' and exists(select 1 from a02 a02 where a01.a0000=a02.a0000 and a0281='true' and A0201B='%s') order by (select lpad(max(a0225),5,'0') from a02 where a01.a0000=a02.a0000 and a0281='true' and A0201B='%s')` var CodeValueMap = make(map[string]string) func RunMc() (string, bool) { db, _ := sql.Open("mysql", url) codes, headers, types, headWidth, err := getInit() if err { showMsg(fmt.Sprintf("导出名册发生异常!")) return "", err } var allObj [][]interface{} if len(CodeValueMap) == 0 { QueryMap(db, fmt.Sprintf(sqlCodeStr, strings.Join(types, "','")), CodeValueMap) } b01s := QueryB01(db) b01 := dept{ Id: "-1", Child: make([]*dept, 0), } makeTree(b01s, &b01) makeA01s(b01, db, codes, types, &allObj) return getExcelFile(headers, headWidth, allObj), false } func makeA01s(b01 dept, db *sql.DB, codes []string, types []string, allObj *[][]interface{}) { for _, d := range b01.Child { QueryA01(db, strings.Join(codes, ","), types, d.Id, allObj) if len(d.Child) > 0 { makeA01s(*d, db, codes, types, allObj) } } } // getInit 初始化 JSON 对象,获取猎头 func getInit() ([]string, []string, []string, []float64, bool) { var yrYhs []YrYh //读取的数据为json格式,需要进行解码 err := json.Unmarshal([]byte(yryh), &yrYhs) if err != nil { showMsg(fmt.Sprintf("导出名册发生异常:%s", err)) return nil, nil, nil, nil, true } yrYhsLen := len(yrYhs) types := make([]string, yrYhsLen, yrYhsLen) // 输出类型 codes := make([]string, yrYhsLen, yrYhsLen) // 输出信息项 headers := make([]string, yrYhsLen, yrYhsLen) // 输出列名 headWidth := make([]float64, yrYhsLen, yrYhsLen) // 输出列名 for i1, i2 := range yrYhs { codes[i1] = i2.Code headers[i1] = i2.Name headWidth[i1] = i2.Width types[i1] = i2.Type } return codes, headers, types, headWidth, false } func getExcelFile(headers []string, headWidth []float64, values [][]interface{}) string { f, _ := ExportExcel(sheetName, headers, headWidth, values) fileNameType := fileName + time.Now().Format("2006102150405") + fileType _ = f.SaveAs(fileNameType) return fileNameType } // maxCharCount 最多26个字符A-Z const maxCharCount = 26 // ExportExcel 导出Excel文件 // sheetName 工作表名称, 注意这里不要取sheet1这种名字,否则导致文件打开时发生部分错误。 // headers 列名切片, 表头 // rows 数据切片,是一个二维数组 func ExportExcel(sheetName string, headers []string, headWidth []float64, rows [][]interface{}) (*excelize.File, error) { f := excelize.NewFile() style, _ := f.NewStyle(`{ "border":[ {"type":"top","color":"#000000","style":1}, {"type":"left","color":"#000000","style":1}, {"type":"right","color":"#000000","style":1}, {"type":"bottom","color":"#000000","style":1} ], "font":{"size":12,"color":"#000000","family":"仿宋"}, "alignment":{"horizontal":"center","vertical":"center","wrap_text":true}, "fill":{"type":"gradient","shading":0,"color":["#FFFF00","#FFFF00"]} }`) sheetIndex := f.NewSheet(sheetName) maxColumnRowNameLen := 1 + len(strconv.Itoa(len(rows))) columnCount := len(headers) if columnCount > maxCharCount { maxColumnRowNameLen++ } else if columnCount > maxCharCount*maxCharCount { maxColumnRowNameLen += 2 } columnNames := make([][]byte, 0, columnCount) f.SetRowHeight(sheetName, 1, 80) // 设置行高 for i, header := range headers { columnName := getColumnName(i, maxColumnRowNameLen) columnNames = append(columnNames, columnName) // 初始化excel表头,这里的index从1开始要注意 curColumnName := getColumnRowName(columnName, 1) f.SetColWidth(sheetName, toCharStr(i+1), toCharStr(i+1), headWidth[i]) f.SetCellValue(sheetName, curColumnName, header) f.SetCellStyle(sheetName, curColumnName, curColumnName, style) // 设置单元格样式 } for rowIndex, row := range rows { for columnIndex, columnName := range columnNames { // 从第二行开始 f.SetCellValue(sheetName, getColumnRowName(columnName, rowIndex+2), row[columnIndex]) } } f.SetActiveSheet(sheetIndex) return f, nil } // getColumnName 生成列名 // Excel的列名规则是从A-Z往后排;超过Z以后用两个字母表示,比如AA,AB,AC;两个字母不够以后用三个字母表示,比如AAA,AAB,AAC // 这里做数字到列名的映射:0 -> A, 1 -> B, 2 -> C // maxColumnRowNameLen 表示名称框的最大长度,假设数据是10行,1000列,则最后一个名称框是J1000(如果有表头,则是J1001),是4位 // 这里根据 maxColumnRowNameLen 生成切片,后面生成名称框的时候可以复用这个切片,而无需扩容 func getColumnName(column, maxColumnRowNameLen int) []byte { const A = 'A' if column < maxCharCount { // 第一次就分配好切片的容量 slice := make([]byte, 0, maxColumnRowNameLen) return append(slice, byte(A+column)) } else { // 递归生成类似AA,AB,AAA,AAB这种形式的列名 return append(getColumnName(column/maxCharCount-1, maxColumnRowNameLen), byte(A+column%maxCharCount)) } } // getColumnRowName 生成名称框 // Excel的名称框是用A1,A2,B1,B2来表示的,这里需要传入前一步生成的列名切片,然后直接加上行索引来生成名称框,就无需每次分配内存 func getColumnRowName(columnName []byte, rowIndex int) (columnRowName string) { l := len(columnName) columnName = strconv.AppendInt(columnName, int64(rowIndex), 10) columnRowName = string(columnName) // 将列名恢复回去 columnName = columnName[:l] return } // QueryB01 获取机构树信息 func QueryB01(db *sql.DB) []*dept { rows, _ := db.Query(sqlB01) defer rows.Close() var b0111, b0121, b0101 string depts := make([]*dept, 0) for rows.Next() { _ = rows.Scan(&b0111, &b0121, &b0101) depts = append(depts, &dept{ Id: b0111, Pid: b0121, Name: b0101, Child: make([]*dept, 0), }) } return depts } // QueryA01 获取人员信息 func QueryA01(db *sql.DB, codes string, types []string, b0111 string, allObj *[][]interface{}) { var sqlStr string if typeStr == "1" { sqlStr = fmt.Sprintf(sqlA01A0279, codes, b0111, b0111) } else { sqlStr = fmt.Sprintf(sqlA01A0281, codes, b0111, b0111) } rows, _ := db.Query(sqlStr) defer rows.Close() columns, _ := rows.Columns() columnsLen := len(columns) scanArgs := make([]interface{}, columnsLen) values := make([]interface{}, columnsLen) for i := range values { scanArgs[i] = &values[i] } for rows.Next() { _ = rows.Scan(scanArgs...) valuer := make([]interface{}, columnsLen) setValueStr(values, types, valuer) *allObj = append(*allObj, valuer) } } func setValueStr(values []interface{}, types []string, valuer []interface{}) { for i, col := range values { if col != nil { s := string(col.([]byte)) if types[i] == NULL { valuer[i] = s } else if types[i] == DATE { valuer[i] = s[0:4] + DIAN + s[4:6] } else { valuer[i] = CodeValueMap[types[i]+s] } } else { valuer[i] = NULL } } } // 获取表数据 func QueryMap(db *sql.DB, sqlStr string, m map[string]string) { rows, _ := db.Query(sqlStr) defer rows.Close() columns, _ := rows.Columns() for rows.Next() { _ = rows.Scan(&columns[0], &columns[1]) m[columns[0]] = columns[1] } } func has(v1 dept, vs []*dept) bool { var has bool has = false for _, v2 := range vs { v3 := *v2 if v1.Id == v3.Pid { has = true break } } return has } // makeTree 生成机构树对象 func makeTree(vs []*dept, node *dept) { childs := findChild(node, vs) for _, child := range childs { node.Child = append(node.Child, child) if has(*child, vs) { makeTree(vs, child) } } } func findChild(v *dept, vs []*dept) (ret []*dept) { ret = make([]*dept, 0) for _, v2 := range vs { if v.Id == v2.Pid { ret = append(ret, v2) } } return } // 数字转字母 func toCharStr(i int) string { return string(rune('A' - 1 + i)) } type YrYh struct { Name string `json:"name"` Code string `json:"code"` Type string `json:"type"` Width float64 `json:"width"` } const yryh = `[ {"name":"姓名","code":"a01.a0101","type":"","width":11}, {"name":"性别","code":"a01.a0104","type":"GB2261","width":8}, {"name":"民族","code":"a01.a0117","type":"GB3304","width":8}, {"name":"出生日期","code":"a01.a0107","type":"DATE","width":12}, {"name":"参加工作时间","code":"a01.a0134","type":"DATE","width":13}, {"name":"入党时间","code":"a01.a0140","type":"","width":12}, {"name":"工作单位及职务全称","code":"a01.a0192a","type":"","width":20}, {"name":"职务层次","code":"a01.a0221","type":"ZB09","width":12}, {"name":"任职务层次时间","code":"a01.a0288","type":"DATE","width":15}, {"name":"职级","code":"a01.a0192e","type":"ZB148","width":12}, {"name":"任职级时间","code":"a01.a0192c","type":"DATE","width":12}, {"name":"任相当层次职务职级时间","code":"a01.a0192x","type":"DATE","width":15}, {"name":"最高学历","code":"a01.zgxl","type":"","width":15}, {"name":"籍贯","code":"a01.a0111","type":"ZB01","width":12} ]` const DATE = "DATE" const NULL = "" const DIAN = "." // 树结构 type dept struct { Id string `json:"id"` Pid string `json:"pid"` Name string `json:"name"` Child []*dept `json:"child"` }