diff --git a/golang_learn/data_func/go_to_windows/main.go b/golang_learn/data_func/go_to_windows/main.go index 6c9ddad..9607bbb 100644 --- a/golang_learn/data_func/go_to_windows/main.go +++ b/golang_learn/data_func/go_to_windows/main.go @@ -32,6 +32,8 @@ var passwordname = "Qw^e1@10&++Hzb...?8" // 解压密码 var path string // 该部分不存在最后 横线 (格式为 D:\hzb2021),使用时需要补充横线 var epsoft1 = `全国公务员管理信息系统2020版` // 系统名称 1 var epsoft2 = `全国公务员管理信息系统2021版` // 系统名称 2 +var url = `root:admin@tcp(127.0.0.1:35017)/hy_qggwy?charset=utf8` +var typeStr = `0` // 执行导出名册默认 0-按照职务输出生成 1-按照主职务生成 var timezone string type myApp struct { @@ -431,6 +433,28 @@ func setDbSize() { }) } +// 按照职务输出生成(可能人员重复) +func getMcByA0281() { + typeStr = "0" + mc, b := RunMc() + if b { + return + } + showMsg("导出文件成功!") + showMsg(fmt.Sprintf("存放当前目录下,文件名称:%s", mc)) +} + +// 按照主职务生成(可能人员丢失) +func getMcByA0279() { + typeStr = "1" + mc, b := RunMc() + if b { + return + } + showMsg("导出文件成功!") + showMsg(fmt.Sprintf("存放当前目录下,文件名称:%s", mc)) +} + func setDbIni(innodbBufferPoolSize int) { //读写方式打开文件 myIniFile := fmt.Sprintf(`%s/mysql/my.ini`, path) @@ -496,7 +520,7 @@ func reDbIndex() { time.Sleep(time.Second) - db, _ := sql.Open("mysql", "root:admin@tcp(127.0.0.1:35017)/hy_qggwy?charset=utf8") + db, _ := sql.Open("mysql", url) showMsg("连接数据库成功!\n请不要进行其他操作,否则影响执行...") for i := 0; i < len(DbIndex); i++ { ExecSql(db, fmt.Sprintf(`DROP INDEX %s ON %s`, DbIndex[i].idxName, DbIndex[i].tabName)) @@ -526,7 +550,7 @@ func dbBak() { // 判断 mysql 服务应用启动 if !service1.btnStart.Enabled() { - db, _ := sql.Open("mysql", "root:admin@tcp(127.0.0.1:35017)/hy_qggwy?charset=utf8") + db, _ := sql.Open("mysql", url) showMsg("连接数据库成功!\n备份操作可能需要相当长时间...") // 【压缩data方式】创建临时文件夹 diff --git a/golang_learn/data_func/go_to_windows/winMc.go b/golang_learn/data_func/go_to_windows/winMc.go new file mode 100644 index 0000000..58a7583 --- /dev/null +++ b/golang_learn/data_func/go_to_windows/winMc.go @@ -0,0 +1,309 @@ +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"` +} diff --git a/golang_learn/data_func/go_to_windows/winView.go b/golang_learn/data_func/go_to_windows/winView.go index 13ecce5..3b404af 100644 --- a/golang_learn/data_func/go_to_windows/winView.go +++ b/golang_learn/data_func/go_to_windows/winView.go @@ -62,6 +62,23 @@ func GetWindows() error { }, }, }, + Menu{ + Text: "&表册", + // Image: "./image/shezhi.png", + Items: []MenuItem{ + Separator{}, + Action{ + // Image: "./image/shujuku.png", + Text: "按照职务输出生成(可能人员重复)", + OnTriggered: getMcByA0281, + }, + Action{ + // Image: "./image/yingyong.png", + Text: "按照主职务生成(可能人员丢失)", + OnTriggered: getMcByA0279, + }, + }, + }, Menu{ Text: "&帮助", Items: []MenuItem{