在上一篇文章中,介绍了小工具【代码生成工具】中的任务1【解析数据字典文件】实现过程,在实现过程当中对Go语言进行了学习,具体学习的内容以下:html
咱们已经生成了schema.json文件,接下来的任务就是将模板加载进来,而后在解析这个schema.json的过程当中完成实际代码的生成。golang
审视一下以前的代码,有以下发现:shell
那么,在开始新任务以前,最好是将基于不一样的函数功能,将单文件拆分红多文件,以增长可读性。新功能的代码,即可以直接再新的对应子文件中进行编写了。json
拆分后的文件以下(终端中执行,➜ a_code_generator 是当前目录):数组
➜ a_code_generator tree main
main
├── console.go
├── json.go
├── main.go
├── xlsx.go
└── xlsx2schema.go
复制代码
每一个文件中包含的内容介绍以下:app
斜体与加粗的部分是比上一篇文章中有所改变的方法框架
main.go 中的 main 方法改造后的结果,以下:mongoose
package main
// 入口函数
func main() {
println("程序运行 @ 开始")
// 1.接收控制台变量
fileName, sheetName, sheetIndex, err := receiveConsoleParam()
if err != nil {
println(err.Error())
return
}
// 2.xlsx 文件转换成 schema 文件
xlsx2schema(fileName, sheetName, sheetIndex)
println("程序运行 @ 结束")
}
复制代码
函数xlsx2schema的方法定义以下:函数
// xlsx 内容 转换成 schema 文件
func xlsx2schema(fileName string, sheetName string, sheetIndex int) {
// 1.打开xlsx文件
f, err := excelize.OpenFile(fileName)
if err != nil {
println(err.Error())
return
}
// 2.若是 sheetName 为空 或 sheetIndex 为默认值,则打印出该文件的全部sheet页信息
if sheetName == "" && sheetIndex == -1 {
listAllSheet(f)
return
}
// 3.对xlsx文件中的指定名称的sheet页逐行逐单元格进行遍历
var rows [][]string
if sheetName != "" { // 3.1.当sheet页名称设置时,以 sheetName 为准
rows = f.GetRows(sheetName)
} else { // 3.2.当sheet页名称未设置时,以 sheetIndex 为准
rows = f.GetRows(f.GetSheetName(sheetIndex))
}
err = analyzeSheet(rows)
if err != nil {
println(err.Error())
return
}
}
复制代码
将单文件拆分红多文件以后,程序的运行命令也须要作相应的改变,以下(终端中执行,➜ a_code_generator 是当前目录):工具
➜ a_code_generator go run main/* -f ./resource/datadict.xlsx -i 2
复制代码
这里涉及到的知识点以下:
模板文件实际上就是有两部分组成的,分别是静态内容和动态内容。静态内容是固定不变的部分,动态内容则是在实际生成过程当中须要进行替换的部分,这一部分就是模板中的变量,通常会使用专门的模板语言进行编写,由专门的模板引擎进行处理。经过搜索,发现了三个具备参考性的文章,以下:
经过阅读文章,得知Go语言给咱们提供了两个库来处理模板(text/template和html/template),咱们要处理的模板是NodeJs的文本,使用 text/template 便可。
模板示例以下(model.tmpl):
'use strict';
const path = require("path");
const mongoose = require('mongoose');
const CommonDao = require('./commondao.js')
const dbconn = require('./index.js');
const ObjectId = mongoose.Schema.Types.ObjectId;
class Model extends CommonDao {
constructor(model) { super(model); }
getSchema() { return Schema; }
getConn() { return dbconn.mongoConn; }
getCollect() {
let file = __filename.split(path.sep);
file = file[file.length - 1].split('.')[0];
return file;
}
}
cont Schema = Model.SchemaExt({
{{.}}
});
module.exports = new Model(null);
复制代码
最后面的 {{.}} ,即是要替换的内容,只有这一处,所以,没有用不一样的变量标识。点"."表明当前做用域的当前对象。
schema文件已经在前一步骤中生成出来了,接下来使用程序将其用程序打开读取文件内容(json),并将内容反向解析成 数据字典集合,而后在集合遍历中进行处理便可。代码框架以下:
// 分析模式文件
func analyzeSchema(schemaPath string, modelTmplPath string, controllerTmplPath string) error {
// 1.加载 模式文件
schema, err := ioutil.ReadFile(schemaPath)
if err != nil {
return err
}
// 2.转换 模式文件到 数据字典集合中
var dataDictSlice []DataDict // 数据字典集合
if err := Json.Unmarshal(schema, &dataDictSlice); err != nil {
return err
}
// 3.加载模板文件
modelTmpl, _ := template.ParseFiles(modelTmplPath)
controllerTmpl, _ := template.ParseFiles(controllerTmplPath)
// 4.遍历数据字典集合
for _, dataDict := range dataDictSlice {
// 3.1.生成 model 代码片断
if err := generateModelFile(modelTmpl, dataDict); err != nil {
return err
}
// 3.2.生成 controller 代码片断
if err := generateControllerFile(controllerTmpl, dataDict); err != nil {
return err
}
}
// 5.没有出错,返回 nil
return nil
}
复制代码
在遍历过程当中,用 generateModelFile 和 generateControllerFile 承载了生成代码文件的职责。为了可以复用模板文件,须要将模板文件再遍历前提早加载。analyzeSchema 中涉及到的知识点以下:
- 使用 ioutil.ReadFile 读取 schema.json
- 使用 Json.Unmarshal 将 json 文本解析为 strcut对象 集合
- 使用 template.ParseFiles 加载模板文件(text/template)
- 遍历 结构体数组
在 step_01 中能够看到 model.tmpl 的内容,须要填充的部分就是数据字典下各个字段的定义,所以,咱们只须要在程序中遍历数据字典的字段集合,而且根据约定的规则将全部的字段信息转换拼接成代码片断,而后经过模板引擎将其替换到相应的位置后,输出成文件便可。代码框架以下:
// 生成 model 文件
func generateModelFile(modelTmpl *template.Template, dataDict DataDict) error {
// 1.获取 fields
fields := dataDict.Fields
// 2.将 map 的 key 升序处理
var keys []int
for key := range fields {
iKey, err := strconv.Atoi(key)
if err != nil {
return err
}
keys = append(keys, iKey)
}
sort.Ints(keys)
// 3.顺序遍历并生成 model 代码块
var modelScript strings.Builder
for _, key := range keys {
field := fields[strconv.Itoa(key)]
// 使用 generateFieldCode 来解析并生成每个 field 对应的代码
modelScript.WriteString(generateFieldCode(field))
}
// 4.建立代码文件
modelFile, err := os.Create("./dist/model/" + dataDict.Collection.Name + ".js")
if err != nil {
return nil
}
// 5.模板解析
modelTmpl.Execute(modelFile, modelScript.String())
// 6.关闭文件
defer modelFile.Close()
// 7.没有错误,则返回 nil
return nil
}
复制代码
fields是个 map,是无序的,若是须要 顺序遍历,须要将 key 转存到数组中且进行排序后,经过遍历key数组一一获取 map 中的元素来实现。在遍历过程当中,使用 generateFieldCode 来对字段基于约定的规则转换成相应的代码,不一样的状况下,此方法的逻辑是不同的,在此文中就不在叙述了。generateModelFile 中涉及到的知识点以下:
- map 的顺序遍历
- string类型转int类型 及 int类型转string类型
- strings.Builder 的使用方式
- 建立文件的方式
- 解析模板的方式
- defer的使用方法
不一样的状况下,此方法的逻辑是不同的,在此文中就再也不叙述了。
此方法与 generateModelFile 是相似的,在此文中就再也不叙述了。
结合上一篇文章,到此处,咱们已经实现了从读取 xlsx 格式的数据字典开始,到生成 schema 文件,到最后生成代码文件的完整过程。在这个过程当中,边查边写的了解了Go语言的一些基本特性,虽然距离熟悉Go语言还相差甚远,但也收获了一个可用的代码生成工具,也算是一个好的开头了。