267 lines
8.4 KiB
Go
267 lines
8.4 KiB
Go
package reverse
|
|
|
|
import (
|
|
"ReverseEngine/entity"
|
|
"ReverseEngine/imp"
|
|
"ReverseEngine/static"
|
|
"ReverseEngine/support"
|
|
"ReverseEngine/util"
|
|
"bufio"
|
|
"database/sql"
|
|
"fmt"
|
|
_ "github.com/go-sql-driver/mysql"
|
|
"gopkg.in/ini.v1"
|
|
"log"
|
|
"os"
|
|
"strings"
|
|
)
|
|
|
|
var (
|
|
//数据库到实体映射 可在config.ini中配置
|
|
tm = make(map[string]string)
|
|
|
|
//通用配置 可在config.ini中配置
|
|
def = "string" //默认的映射类型
|
|
tagKey = "xorm" //tag的key
|
|
suffix = ".go" //文件后缀
|
|
cover = true //存在文件是否进行覆盖,只有配置为false的时候不会覆盖,其他默认为true
|
|
|
|
//数据库配置 可在config.ini中配置 格式是:”用户名:密码@tcp(IP:端口)/数据库?charset=utf8”
|
|
username = "你的mysql连接账号"
|
|
password = "你的连接密码"
|
|
tcp = "@tcp"
|
|
ip = "你的地址"
|
|
port = "3306"
|
|
database = "你的数据库" //这个是我的数据库
|
|
charset = "utf8mb4&parseTime=True&loc=Local"
|
|
connMaxLifeTime = 100
|
|
maxIdleConns = 10
|
|
|
|
//校验、数据库连接实现类
|
|
cg imp.ContentGenerate
|
|
dbc imp.DbConnection
|
|
rc imp.ReverseCheck
|
|
)
|
|
|
|
//---------------------------------------初始化,策略配置,规则校验-----------------------------------------
|
|
|
|
type DataReverseEngine struct {
|
|
}
|
|
|
|
func NewEngine() *DataReverseEngine {
|
|
return &DataReverseEngine{}
|
|
}
|
|
|
|
//创建初始化 --加载配置信息等
|
|
func init() {
|
|
cfg := loadIniCfg() //获取ini配置文件的对象
|
|
if nil == cfg {
|
|
panic("配置文件 config.ini配置错误,请检查")
|
|
return
|
|
}
|
|
loadCommonConfig(cfg) //加载通用配置
|
|
loadDataBaseConfig(cfg) //加载数据库配置
|
|
loadTypeMappingConfig(cfg) //加载类型映射配置
|
|
rc = &support.CheckSupport{} //校验支持
|
|
dbc = &support.DbSupport{} //数据库支持
|
|
cg = &support.FileContentSupport{} //文本拼接支持
|
|
|
|
}
|
|
|
|
/*
|
|
重写自己的文件类型定义实现
|
|
*/
|
|
func (*DataReverseEngine) SetContentGenerate(face imp.ContentGenerate) {
|
|
cg = face
|
|
}
|
|
|
|
/*
|
|
重写自己的数据库连接工具
|
|
*/
|
|
func (*DataReverseEngine) SetDbConnection(face imp.DbConnection) {
|
|
dbc = face
|
|
}
|
|
|
|
/*
|
|
重写自己的校验规则
|
|
*/
|
|
func (*DataReverseEngine) SetReverseCheck(face imp.ReverseCheck) {
|
|
rc = face
|
|
}
|
|
|
|
func checkError(err error, s string) {
|
|
if err != nil {
|
|
panic(s + err.Error())
|
|
}
|
|
}
|
|
|
|
//----------------------------------逆向工程---------------------------------
|
|
|
|
/*
|
|
数据库逆向工程
|
|
tableName:表名
|
|
path:创建目录的相对路径(右键选中文件夹,点击relative path得到的路径)
|
|
*/
|
|
func (r *DataReverseEngine) SimpleEngineer(tableName string, path string) {
|
|
r.Engineer(database, tableName, path, cover)
|
|
}
|
|
|
|
/*
|
|
数据库逆向工程
|
|
tableName:表名
|
|
path:创建目录的相对路径(右键选中文件夹,点击relative path得到的路径)
|
|
cover:如果已经存在改文件是否进行覆盖
|
|
*/
|
|
func (r *DataReverseEngine) SimEngineer(tableName string, path string, cover bool) {
|
|
r.Engineer(database, tableName, path, cover)
|
|
}
|
|
|
|
/*
|
|
数据库逆向工程
|
|
dbName:数据库名称
|
|
tableName:表名
|
|
path:创建目录的相对路径(右键选中文件夹,点击relative path得到的路径)
|
|
cover:如果已经存在改文件是否进行覆盖
|
|
*/
|
|
func (*DataReverseEngine) Engineer(dbName string, tableName string, path string, cover bool) {
|
|
//tableName = strings.Title(tableName) //tableName进行首字母大写
|
|
if !rc.CheckFileDir(path, tableName, cover) { //进行通用校验
|
|
return
|
|
}
|
|
dbPath := strings.Join([]string{username, ":", password, tcp, "(", ip, ":", port, ")/", database, "?", "charset=", charset}, "")
|
|
db := dbc.CreateDbConnection(dbPath, maxIdleConns, connMaxLifeTime) //连接数据库--可配置连接不同的数据库
|
|
if db == nil {
|
|
log.Println("数据库连接失败,程序终止")
|
|
return
|
|
}
|
|
//校验对应的数据库表是否存在
|
|
if rc.CheckTableExist(dbName, tableName, db) {
|
|
dbReverse(path, tableName, dbName, db)
|
|
return
|
|
}
|
|
log.Println("数据库表不存在:", tableName) //不存在的话打印日志返回
|
|
}
|
|
|
|
func dbReverse(path string, tableName string, dbName string, db *sql.DB) {
|
|
file, result := util.CreateNeedOpenFile(util.GenerateFilePath(path, tableName, static.Splice, suffix))
|
|
if !result {
|
|
return
|
|
}
|
|
cms := util.FindColumnMessage(dbName, tableName, db) //查询数据库表字段信息
|
|
tableComment := util.FindTableComment(dbName, tableName, db) //查询数据库表字段信息
|
|
dErr := db.Close() //关闭数据库连接
|
|
checkError(dErr, "数据库关闭失败:")
|
|
if nil == cms {
|
|
log.Println("没有查到数据库字段信息略过")
|
|
}
|
|
fms, hasTime := buildFieldMessage(cms) //创建字段名和类型的映射
|
|
w := bufio.NewWriter(file) //进行文件的操作
|
|
content := cg.GenerateFileContent(path, tableName, fms, hasTime, tagKey, tableComment) //拼go文件
|
|
_, err := w.WriteString(content)
|
|
checkError(err, "写入出错了:")
|
|
fErr, cErr := w.Flush(), file.Close()
|
|
|
|
checkError(fErr, "文件写入出错了,flush错误:")
|
|
checkError(cErr, "文件写入出错了,close错误:")
|
|
}
|
|
|
|
func buildFieldMessage(cms []entity.ColumnMessage) ([]entity.FieldMessage, bool) {
|
|
var fms []entity.FieldMessage
|
|
hasTime := false
|
|
|
|
for _, cm := range cms {
|
|
fm := &entity.FieldMessage{
|
|
FiledName: cm.Field, //字段名称
|
|
FieldType: typeMapping(cm.Type, &hasTime), //字段类型,进行映射
|
|
OriginType: cm.Type, //数据库原始类型
|
|
IsKey: cm.IsKey(), //是否主键
|
|
TagKey: tagKey, //tag的key值,这边先写死,正常应该是可配
|
|
Comment: cm.GetComment(), //注释信息
|
|
Default: cm.GetDefault(), //默认值信息
|
|
CanNull: cm.CanNull(), //是否可以为空
|
|
}
|
|
|
|
fms = append(fms, *fm)
|
|
}
|
|
|
|
return fms, hasTime
|
|
}
|
|
|
|
func typeMapping(t string, has *bool) string {
|
|
rt := strings.Split(t, "(")[0] //需要把类型后面的([长度])去掉
|
|
value, ok := tm[rt]
|
|
if ok {
|
|
if value == static.TimeT {
|
|
*has = true
|
|
}
|
|
return value
|
|
}
|
|
|
|
return def
|
|
}
|
|
|
|
//-----------------------------------读取和属性设置-----------------------------
|
|
func loadIniCfg() *ini.File {
|
|
cfg, err := ini.Load(static.IniAddress)
|
|
if err != nil {
|
|
log.Println("读取失败使用原始配置", err)
|
|
os.Exit(1)
|
|
return nil
|
|
}
|
|
return cfg
|
|
}
|
|
|
|
func loadDataBaseConfig(cfg *ini.File) {
|
|
if len(cfg.Section(static.DataBase).Keys()) == 0 {
|
|
log.Println("配置区间为空,采用数据库配置通用配置")
|
|
return
|
|
}
|
|
util.GenerateConfig("username", username, cfg.Section(static.DataBase))
|
|
util.GenerateConfig("password", password, cfg.Section(static.DataBase))
|
|
util.GenerateConfig("tcp", tcp, cfg.Section(static.DataBase))
|
|
util.GenerateConfig("ip", ip, cfg.Section(static.DataBase))
|
|
util.GenerateConfig("port", port, cfg.Section(static.DataBase))
|
|
util.GenerateConfig("database", database, cfg.Section(static.DataBase))
|
|
util.GenerateConfig("charset", charset, cfg.Section(static.DataBase))
|
|
util.GenerateConfigInt("connMaxLifeTime", connMaxLifeTime, cfg.Section(static.DataBase))
|
|
util.GenerateConfigInt("maxIdleConns", maxIdleConns, cfg.Section(static.DataBase))
|
|
|
|
}
|
|
|
|
func loadCommonConfig(cfg *ini.File) {
|
|
if len(cfg.Section(static.Common).Keys()) == 0 {
|
|
log.Println("配置区间为空,采用通用配置")
|
|
return
|
|
}
|
|
util.GenerateConfig("def", def, cfg.Section(static.Common)) //默认的映射类型
|
|
util.GenerateConfig("tagKey", tagKey, cfg.Section(static.Common)) //tag的key
|
|
util.GenerateConfig("suffix", suffix, cfg.Section(static.Common)) //文件后缀
|
|
util.GenerateConfigBool("cover", &cover, cfg.Section(static.Common)) //存在文件是否进行覆盖,只有配置为false的时候不会覆盖,其他默认为true
|
|
}
|
|
|
|
func defaultTypeMappingConfig() {
|
|
tm["double"] = "float64"
|
|
tm["float"] = "float64"
|
|
tm["int"] = "int"
|
|
tm["tinyint"] = "int"
|
|
tm["bigint"] = "int"
|
|
tm["time"] = "time.Time"
|
|
tm["timestamp"] = "time.Time"
|
|
tm["date"] = "time.Time"
|
|
tm["dateTime"] = "time.Time"
|
|
}
|
|
|
|
func loadTypeMappingConfig(cfg *ini.File) {
|
|
if len(cfg.Section(static.MapType).Keys()) == 0 {
|
|
defaultTypeMappingConfig()
|
|
log.Println("配置区间为空,采用类型映射通用配置")
|
|
return
|
|
}
|
|
|
|
for _, k := range cfg.Section(static.MapType).Keys() {
|
|
if k.Name() != "" {
|
|
tm[k.Name()] = k.Value()
|
|
}
|
|
}
|
|
}
|