feat(ReverseEngine):初始化工程

This commit is contained in:
huangzj
2020-04-26 11:01:26 +08:00
commit a84341447a
22 changed files with 1199 additions and 0 deletions
+101
View File
@@ -0,0 +1,101 @@
package main
import (
"ReverseEngine/imp"
"ReverseEngine/reverse"
"ReverseEngine/support"
"ReverseEngine/util"
"bufio"
"flag"
"fmt"
"os"
"reflect"
"strings"
)
var database string
var tableName string
var filepath string
var cover bool
func main() {
//转换函数
reverseMain()
}
func reverseMain() {
//报错是否直接停止程序,有的时候就是说,我可能输错了,但是我不想停止程序,那就会尴尬,这边配置一个报错不停止的标识
panicStop := flag.Bool("panicStop", false, "错误是否进行停止")
flag.Parse()
conti := true
reader := bufio.NewReader(os.Stdin)
fmt.Print("逆向工程生成中")
fmt.Print("请出入你需要操作的数据库:\n")
database, _ = reader.ReadString('\n')
database = strings.Trim(database, "\n")
e := reverse.NewEngine()
e.SetReverseCheck(&support.CheckPathSupport{})
fmt.Println(runOn(conti, reader, e, *panicStop))
}
func runOn(conti bool, reader *bufio.Reader, e *reverse.DataReverseEngine, panicStop bool) string {
defer func() {
if panicStop == false || conti == false {
return
}
if err := recover(); err != nil {
util.ColorPrint("程序出错,因为设置panicStop为不停止状态,所以程序继续\n请开始新一轮的反向工程参数设置\n\n\n\n........\n.........\n\n", util.FontColor.Red)
runOn(conti, reader, e, panicStop)
}
}()
for {
if conti == false {
return "程序运行结束"
}
fmt.Print("请出入你需要操作的表名,该名称不区分大小写,输出即为文件名:\n")
tableName, _ = reader.ReadString('\n')
tableName = strings.Trim(tableName, "\n")
fmt.Print("请出入结构体生成位置(绝对路径):\n")
filepath, _ = reader.ReadString('\n')
filepath = strings.Trim(filepath, "\n")
fmt.Print("如果文件存在是否覆盖(Y表示覆盖):\n")
coverS, _ := reader.ReadString('\n')
coverS = strings.Trim(coverS, "\n")
if coverS == "Y" || coverS == "y" {
cover = true
}
e.Engineer(database, tableName, filepath, cover)
fmt.Print("是否停止程序,输入Y表示停止:\n")
stop, _ := reader.ReadString('\n')
stop = strings.Trim(stop, "\n")
if stop == "Y" || stop == "y" {
conti = false
}
}
}
func runOnce() {
e := reverse.NewEngine()
//e.SetContentGenerate(support.FileContentSUpport2{})
e.SetReverseCheck(&support.CheckPathSupport{})
e.Engineer("你的数据库", "你的表名", "F:/self_project/src/ReverseEngine/filepath", true)
//e.Engineer("你的数据库", "testreversing", "filepath", true)
//
//s := support.CheckSupport{}
//fmt.Println(&s)
tx := reflect.ValueOf(support.CheckSupport{}).Type()
fmt.Println(tx)
var ok imp.ReverseCheck = (*support.CheckSupport)(nil)
fmt.Println(ok)
//t := reflect.ValueOf(Test{}).Type()
//fmt.Println(t)
//v := reflect.New(t).Elem()
//fmt.Println(v)
}
+113
View File
@@ -0,0 +1,113 @@
## 查询命令记录
```
查询数据库表的信息sql,这边主要是用来查询表的信息:
select * from information_schema.TABLES where TABLE_SCHEMA='[数据库名]'
查询数据库表的字段元数据信息sql:
show full columns from [数据库名].[表名]
查询结果:
Field(字段名) Type(字段类型) Collation NULL(能否为空) Key(是否主键) Default(默认值) Extra Privilege Comment(注释)
```
## 使用ini配置文件读取工具
```
读取ini配置文件的工具:https://ini.unknwon.io/docs/intro/getting_started
配置信息保存路径:reverse/config.ini
支持三类配置:
[database] //数据库配置
[common] //通用配置
[type_mapping] //类型映射
按照对应格式进行配置,如果没有找到对应配置则使用数据库通用配置
```
## 可拓展支持
```
可拓展支持三个接口
ContentGenerate:文件内容拼接
DbConnection 数据库支持
ReverseCheck 校验支持
在Engine中通过以下三个方法实现配置
func SetContentGenerate
func SetDbConnection
func SetReverseCheck
```
## 方法调用说明
```
/**
* 传入表名和生成路径,数据库名和是否覆盖按照配置文件获取
*/
SimpleEngineer(tableName string, path string)
/**
* 传入表名、生成路径和是否覆盖,数据库名读取配置
*/
func (r *DataReverseEngine) SimEngineer(tableName string, path string, cover bool)
/**
* 传入表名、生成路径和是否覆盖、数据库名
*/
func (*DataReverseEngine) Engineer(dbName string, tableName string, path string, cover bool)
```
## 配置文件例子
```
[database] //数据库配置
username = 我的数据库账号
password = 我的数据库密码
tcp = @tcp
ip = 我的数据库机器Ip
port = 3306
database = 我的数据库名
charset = utf8mb4&parseTime=True&loc=Local
connMaxLifeTime = 100
maxIdleConns = 10
[common] //通用配置
def = string //默认的映射类型
tagKey = xorm //tag的key
suffix = .go //文件后缀
cover = true //存在文件是否进行覆盖,只有配置为false的时候不会覆盖,其他默认为true
[type_mapping] //类型映射
double = float64
float = float64
int = int
tinyint = int
bigint = int
time = time.Time
timestamp = time.Time
date = time.Time
dateTime = time.Time
```
## 方法调用例子
```
//使用相对路径
e := reverse.NewEngine()
e.Engineer("数据库名字", "表名", "filepath", true)
//使用绝对路径
e := reverse.NewEngine()
e.SetReverseCheck(&support.CheckPathSupport{})
e.Engineer("数据库名字", "表名", "F:/self_project/src/ReverseEngine/filepath", true)
```
## 问题描述
```
1.数据库连接是通过最原始的方式进行,这边后期可以修改成xorm或者其他的orm框架方式
2.读写文件的方法可以做相应的修改,这边也是采用最原生的方式
```
## 重复使用
直接运行Main.go方法,在控制台根据提示输入对应的数据库名、表名、生成位置、文件存在是否覆盖等参数来达到直接调用函数使用生成的目的
一次生成结束提示输入Y即可结束程序
启动时通过`panicStop`参数输入,控制程序报错不停止,除非在提示结束的时候输入Y.
## 使用
在config.ini中配置你的数据库信息,最好修改Engine中默认的数据库信息,因为这边的数据库信息都是我乱写的
+56
View File
@@ -0,0 +1,56 @@
package entity
import "database/sql"
type ColumnMessage struct {
Field string //字段名称
Type string //字段类型
Collation sql.NullString //编码信息
Null sql.NullString //是否允许为空
Key sql.NullString //是否是主键
Default sql.NullString //默认值
Extra sql.NullString //不懂
Privileges sql.NullString //执行权限
Comment sql.NullString //注释信息
}
/*
返回结果判断是不是主键
*/
func (c *ColumnMessage) IsKey() bool {
if c.Key.Valid && c.Key.String == "PRI" {
return true
}
return false
}
/*
返回结果是否为空信息
*/
func (c *ColumnMessage) CanNull() bool {
if c.Null.Valid && c.Null.String == "NO" {
return false
}
return true
}
/*
返回注释信息,没有注释信息返回空字符串
*/
func (c *ColumnMessage) GetComment() string {
if c.Comment.Valid {
return c.Comment.String
}
return ""
}
/*
返回默认值信息
*/
func (c *ColumnMessage) GetDefault() string {
if c.Default.Valid {
return c.Default.String
}
return ""
}
+15
View File
@@ -0,0 +1,15 @@
package entity
type FieldMessage struct {
FiledName string //类型名称
FieldType string //类型
OriginType string //数据库原始类型
/*
正常情况下tag的组合是 `TagKey:"pk/ not null/ FieldType"`
*/
TagKey string //tag的key
IsKey bool //是否主键
Comment string //注释内容
Default string //默认值信息
CanNull bool //是否可以为空
}
+5
View File
@@ -0,0 +1,5 @@
package entity
type TableMessage struct {
TableName string //数据库表名
}
+26
View File
@@ -0,0 +1,26 @@
package filepath
import (
"time"
)
/*
* @param
* @return
* @description 这个是生成的文件,没有格式化
*/
type Testreversing struct {
t1 int `xorm:"pk not null bigint(20)"` //你好
t2 string `xorm:"not null binary(255)"` //你不好
t3 string `xorm:"bit(64)"` //你是不是啥
t4 string `xorm:"blob"` //是的
t5 string `xorm:"char(255)"` //
t6 time.Time `xorm:"date"` //
t7 string `xorm:"datetime"` //hello
t8 float64 `xorm:"double(255,0)"` //
t9 float64 `xorm:"float(255,0)"` //
t10 int `xorm:"int(255)"` //
t11 time.Time `xorm:"pk not null time"` //
t12 time.Time `xorm:"not null timestamp"` //
t13 string `xorm:"varchar(255)"` //
}
+42
View File
@@ -0,0 +1,42 @@
module ReverseEngine
require (
github.com/DATA-DOG/go-txdb v0.1.2 // indirect
github.com/aceld/zinx v0.0.0-20190612015555-26a73a3ddf2e // indirect
github.com/ahmetb/go-linq v3.0.0+incompatible // indirect
github.com/gin-contrib/sse v0.0.0-20190125020943-a7658810eb74 // indirect
github.com/gin-gonic/gin v1.3.0 // indirect
github.com/go-ini/ini v1.51.1 //read init property
github.com/go-redis/redis v6.15.2+incompatible // indirect
github.com/go-sql-driver/mysql v1.5.0
github.com/go-xorm/cmd v0.0.0-20190426080617-f87981e709a1 // indirect
github.com/go-xorm/xorm v0.7.9 // indirect
github.com/golang/mock v1.3.0 // indirect
github.com/google/go-cmp v0.3.0 // indirect
github.com/hashicorp/golang-lru v0.5.1 // indirect
github.com/json-iterator/go v1.1.6 // indirect
github.com/kr/pretty v0.1.0 // indirect
github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/muesli/cache2go v0.0.0-20180419202730-5a1839810579 // indirect
github.com/nochso/tocenize v0.4.0 // indirect
github.com/oschwald/geoip2-golang v1.2.1 // indirect
github.com/oschwald/maxminddb-golang v1.3.0 // indirect
github.com/panjf2000/ants v1.0.1 // indirect
github.com/panjf2000/ants/v2 v2.2.2 // indirect
github.com/robfig/cron v0.0.0-20180505203441-b41be1df6967 // indirect
github.com/samuel/go-zookeeper v0.0.0-20180130194729-c4fab1ac1bec // indirect
github.com/satori/go.uuid v1.2.0 // indirect
github.com/sirupsen/logrus v1.4.2 // indirect
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a // indirect
github.com/tealeg/xlsx v1.0.3 // indirect
github.com/tidwall/gjson v1.2.1 // indirect
github.com/valyala/fasthttp v1.1.0 // indirect
go.etcd.io/etcd v3.3.13+incompatible // indirect
go.uber.org/zap v1.10.0 // indirect
golang.org/x/net v0.0.0-20190603091049-60506f45cf65 // indirect
gopkg.in/go-playground/assert.v1 v1.2.1 // indirect
gopkg.in/go-playground/validator.v8 v8.18.2 // indirect
gopkg.in/ini.v1 v1.51.1
)
go 1.13
+10
View File
@@ -0,0 +1,10 @@
package imp
import "ReverseEngine/entity"
type ContentGenerate interface {
/*
内容制造接口
*/
GenerateFileContent(filePath string, fileName string, fms []entity.FieldMessage, hasTime bool, tagKey string) string
}
+10
View File
@@ -0,0 +1,10 @@
package imp
import "database/sql"
type DbConnection interface {
/*
数据库连接接口
*/
CreateDbConnection(dbPath string, maxIdleConns int, connMaxLifeTime int) *sql.DB
}
+18
View File
@@ -0,0 +1,18 @@
package imp
import "database/sql"
type ReverseCheck interface {
/*
关于文件和文件夹的一致校验接口
dirPath 文件夹路径
tableName 数据库表名
cover 是否覆盖已有文件
*/
CheckFileDir(dirPath string, tableName string, cover bool) bool
/*
校验数据库表是否存在
*/
CheckTableExist(dbName string, tableName string, db *sql.DB) bool
}
+264
View File
@@ -0,0 +1,264 @@
package reverse
import (
"ReverseEngine/entity"
"ReverseEngine/imp"
"ReverseEngine/static"
"ReverseEngine/support"
"ReverseEngine/util"
"bufio"
"database/sql"
_ "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) //查询数据库表字段信息
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) //拼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()
}
}
}
+28
View File
@@ -0,0 +1,28 @@
[database] //数据库配置
username = 你的mysql连接账号
password = 你的连接密码
tcp = @tcp
ip = 你的地址
port = 3306
database = 你的数据库名
charset = utf8mb4&parseTime=True&loc=Local
connMaxLifeTime = 100
maxIdleConns = 10
[common] //通用配置
def = string //默认的映射类型
tagKey = xorm //tag的key
suffix = .go //文件后缀
cover = true //存在文件是否进行覆盖,只有配置为false的时候不会覆盖,其他默认为true
[type_mapping] //类型映射
double = float64
float = float64
int = int
tinyint = int
bigint = int
time = time.Time
timestamp = time.Time
date = time.Time
datetime = time.Time
+16
View File
@@ -0,0 +1,16 @@
package static
const (
Splice = "/" //文件分割符
LineFeed = "\n" //换行符
LeftBrace = "{" //左大括号
RightBrace = "}" //右大括号
DoubleInclinedRod = "//" //创斜杆
Colon = ":" //冒号
TimeT = "time.Time" //时间类型
IniAddress = "reverse/config.ini" //ini文件地址
Common = "common" //ini文件通用配置的section名
DataBase = "database" //ini文件数据库配置的section名
MapType = "type_mapping" //ini文件类型映射的section名
)
+50
View File
@@ -0,0 +1,50 @@
package support
import (
"ReverseEngine/entity"
"ReverseEngine/static"
"ReverseEngine/util"
"database/sql"
"log"
"reflect"
"strings"
)
type CheckPathSupport struct{}
func (*CheckPathSupport) CheckFileDir(path string, tableName string, cover bool) bool {
//先查询一下路径是否为文件夹
if b := util.IsDirExist(path); !b {
log.Println("文件夹路径不存在略过")
return false
}
//如果是不覆盖的情况,判断是否有这个文件,如果有的话直接返回
if !cover && util.IsFileExist(strings.Join([]string{path, static.Splice, tableName}, "")) {
return false
}
//判断读写权限
if !util.HasReadWritePermission(path) {
log.Println("没有该文件夹的读写权限:", path)
return false
}
return true
}
func (*CheckPathSupport) CheckTableExist(dbName string, tableName string, db *sql.DB) bool {
var tm entity.TableMessage
err := db.QueryRow(strings.Join([]string{"select TABLE_NAME from information_schema.TABLES where TABLE_SCHEMA=? and TABLE_NAME=?"}, ""), dbName, tableName).Scan(&tm.TableName)
//查询失败
if err != nil {
panic("数据库查询失败:" + err.Error())
return false
}
//查询没有数据
if !reflect.ValueOf(tm).IsValid() || tm.TableName == "" {
panic("没有表数据信息:" + tableName)
return false
}
return true
}
+57
View File
@@ -0,0 +1,57 @@
package support
import (
"ReverseEngine/entity"
"ReverseEngine/static"
"ReverseEngine/util"
"database/sql"
"log"
"reflect"
"strings"
)
type CheckSupport struct{}
func (*CheckSupport) CheckFileDir(path string, tableName string, cover bool) bool {
b, realPath := pathExist(path)
//先查询一下路径是否为文件夹
if !b {
log.Println("文件夹路径不存在略过")
return false
}
//如果是不覆盖的情况,判断是否有这个文件,如果有的话直接返回
if !cover && util.IsFileExist(strings.Join([]string{path, static.Splice, tableName}, "")) {
return false
}
//判断读写权限
if !util.HasReadWritePermission(realPath) {
log.Println("没有该文件夹的读写权限:", realPath)
return false
}
return true
}
func (*CheckSupport) CheckTableExist(dbName string, tableName string, db *sql.DB) bool {
var tm entity.TableMessage
err := db.QueryRow(strings.Join([]string{"select TABLE_NAME from information_schema.TABLES where TABLE_SCHEMA=? and TABLE_NAME=?"}, ""), dbName, tableName).Scan(&tm.TableName)
//查询失败
if err != nil {
panic("数据库查询失败:" + err.Error())
return false
}
//查询没有数据
if !reflect.ValueOf(tm).IsValid() || tm.TableName == "" {
panic("没有表数据信息:" + tableName)
return false
}
return true
}
func pathExist(path string) (bool, string) {
pp := util.GetCurrentPath()
realPath := strings.Join([]string{pp, static.Splice, path}, "")
return util.IsDirExist(strings.Join([]string{pp, static.Splice, path}, "")), realPath
}
+28
View File
@@ -0,0 +1,28 @@
package support
import (
"database/sql"
_ "github.com/go-sql-driver/mysql"
"log"
"time"
)
type DbSupport struct{}
func (dbc *DbSupport) CreateDbConnection(dbPath string, maxIdleConns int, connMaxLifeTime int) *sql.DB {
var db *sql.DB
db, _ = sql.Open("mysql", dbPath)
//设置数据库最大连接数
db.SetConnMaxLifetime(time.Duration(connMaxLifeTime))
//设置上数据库最大闲置连接数
db.SetMaxIdleConns(maxIdleConns)
if err := db.Ping(); err != nil {
panic("数据库连接失败")
return nil
}
log.Println("数据库连接成功")
return db
}
+101
View File
@@ -0,0 +1,101 @@
package support
import (
"ReverseEngine/entity"
"ReverseEngine/static"
"strings"
)
type FileContentSupport struct{}
func (FileContentSupport) GenerateFileContent(filePath string, fileName string, fms []entity.FieldMessage, hasTime bool, tagKey string) string {
var build strings.Builder
pName := splitLast(filePath, static.Splice)
writePackage(&build, pName) //写文件的引用包
writeImportIfTime(&build, hasTime) //如果有time.Time类型的需要写入import
buildStructHead(&build, fileName) //写入结构体头
//对于没有字段的返回空定义的文件
if len(fms) == 0 {
build.WriteString(static.RightBrace)
return build.String()
}
//写中间字段的操作
for _, fm := range fms {
build.WriteString(" ")
build.WriteString(fm.FiledName)
build.WriteString(" ")
build.WriteString(fm.FieldType)
build.WriteString(" ")
build.WriteString(generateTag(fm, tagKey))
build.WriteString(" ")
build.WriteString(static.DoubleInclinedRod)
build.WriteString(fm.Comment)
build.WriteString(static.LineFeed)
}
build.WriteString(static.RightBrace)
return build.String()
}
func generateTag(message entity.FieldMessage, tagKey string) string {
var build strings.Builder
build.WriteString("`")
build.WriteString(tagKey)
build.WriteString(static.Colon)
generateTagValue(message, &build)
build.WriteString("`")
return build.String()
}
func generateTagValue(message entity.FieldMessage, builder *strings.Builder) {
builder.WriteString("\"")
if message.IsKey {
builder.WriteString("pk ")
}
if !message.CanNull {
builder.WriteString("not null ")
}
builder.WriteString(message.OriginType)
builder.WriteString("\"")
}
func buildStructHead(build *strings.Builder, fileName string) {
build.WriteString("type ")
build.WriteString(fileName)
build.WriteString(" struct")
build.WriteString(static.LeftBrace)
build.WriteString(static.LineFeed)
}
func writeImportIfTime(build *strings.Builder, hasTime bool) {
//如果有类型匹配上时间,那么需要在头部加上import
if hasTime {
build.WriteString("import (")
build.WriteString("\n ")
build.WriteString("\"time\"")
build.WriteString("\n")
build.WriteString(")")
build.WriteString(static.LineFeed)
build.WriteString(static.LineFeed)
}
}
func writePackage(build *strings.Builder, pName string) {
build.WriteString("package ")
build.WriteString(pName)
build.WriteString(static.LineFeed)
build.WriteString(static.LineFeed)
}
/*
返回按照规则切割的最后一个字符串
*/
func splitLast(filePath string, split string) string {
ss := strings.Split(filePath, split)
return ss[len(ss)-1]
}
+13
View File
@@ -0,0 +1,13 @@
package support
import (
"ReverseEngine/entity"
"fmt"
)
type FileContentSUpport2 struct{}
func (FileContentSUpport2) GenerateFileContent(filePath string, fileName string, fms []entity.FieldMessage, hasTime bool, tagKey string) string {
fmt.Println("12321321")
return " "
}
+44
View File
@@ -0,0 +1,44 @@
/*
* @Author : huangzj
* @Time : 2020/4/17 14:04
* @Description
*/
package util
import "syscall"
var (
kernel32 *syscall.LazyDLL = syscall.NewLazyDLL(`kernel32.dll`)
proc *syscall.LazyProc = kernel32.NewProc(`SetConsoleTextAttribute`)
CloseHandle *syscall.LazyProc = kernel32.NewProc(`CloseHandle`)
// 给字体颜色对象赋值
FontColor Color = Color{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
)
type Color struct {
black int // 黑色
blue int // 蓝色
green int // 绿色
cyan int // 青色
Red int // 红色
purple int // 紫色
yellow int // 黄色
light_gray int // 淡灰色(系统默认值)
gray int // 灰色
light_blue int // 亮蓝色
light_green int // 亮绿色
light_cyan int // 亮青色
light_red int // 亮红色
light_purple int // 亮紫色
light_yellow int // 亮黄色
white int // 白色
}
// 输出有颜色的字体
func ColorPrint(s string, i int) {
handle, _, _ := proc.Call(uintptr(syscall.Stdout), uintptr(i))
print(s)
CloseHandle.Call(handle)
}
+32
View File
@@ -0,0 +1,32 @@
package util
import (
"ReverseEngine/entity"
"database/sql"
"strings"
)
type DbUtil struct {
}
func FindColumnMessage(dbName string, tableName string, db *sql.DB) []entity.ColumnMessage {
var cms []entity.ColumnMessage
row, err := db.Query(strings.Join([]string{"show full columns from ", dbName, ".", tableName}, ""))
if err != nil {
panic("数据库查询出错:" + err.Error())
}
for row.Next() {
var cm entity.ColumnMessage
err := row.Scan(&cm.Field, &cm.Type, &cm.Collation, &cm.Null, &cm.Key, &cm.Default, &cm.Extra, &cm.Privileges, &cm.Comment)
if err != nil {
panic("字段赋值错误了" + err.Error())
return nil
}
cms = append(cms, cm)
}
return cms
}
+135
View File
@@ -0,0 +1,135 @@
package util
import (
"log"
"os"
"strings"
)
/*
获取当前项目的路径
*/
func GetCurrentPath() string {
dir, err := os.Getwd()
if err != nil {
log.Fatal(err)
}
return strings.Replace(dir, "\\", "/", -1)
}
/*
判断路径是否存在并且为文件夹
*/
func IsDirExist(dirPath string) bool {
fi, err := os.Stat(dirPath)
if err != nil {
return false
}
if fi.IsDir() {
return true
}
return false
}
/*
判断路径是否存在并且为文件
*/
func IsFileExist(filePath string) bool {
fi, err := os.Stat(filePath)
//文件不存在或者是文件夹的直接返回false
if err != nil || fi.IsDir() {
return false
}
return true
}
/*
判断是否有读权限
*/
func HasReadPermission(filePath string) bool {
_, err := os.OpenFile(filePath, os.O_RDONLY, 0666)
if err != nil {
if os.IsPermission(err) {
return false
}
}
return true
}
/*
判断是否有写权限
*/
func HasWritePermission(filePath string) bool {
_, err := os.OpenFile(filePath, os.O_WRONLY, 0666)
if err != nil {
if os.IsPermission(err) {
return false
}
}
return true
}
/*
判断是否有读+写权限
*/
func HasReadWritePermission(filePath string) bool {
return HasReadPermission(filePath) && HasWritePermission(filePath)
}
/*
创建文件
*/
func CreateFile(filePath string) (*os.File, bool) {
file, err := os.Create(filePath)
if err != nil {
panic("创建文件失败" + err.Error())
return nil, false
}
return file, true
}
/*
返回拼接的文件路径,这边类的首字母需要大写
dirPath 文件夹路径
filePath 文件路径
splice 路径分隔符
suffix 后缀
*/
func GenerateFilePath(dirPath string, filePath string, splice string, suffix string) string {
return strings.Join([]string{dirPath, splice, strings.Title(filePath), suffix}, "")
}
/*
创建(如果需要)并已读写权限打开文件
os.O_RDONLY // 只读
os.O_WRONLY // 只写
os.O_RDWR // 读写
os.O_APPEND // 往文件中添建(Append
os.O_CREATE // 如果文件不存在则先创建
os.O_TRUNC // 文件打开时裁剪文件
os.O_EXCL // 和O_CREATE一起使用,文件不能存在
os.O_SYNC // 以同步I/O的方式打开
*/
func CreateNeedOpenFile(filePath string) (*os.File, bool) {
//先删除再创建
if IsFileExist(filePath) {
err := os.Remove(filePath)
if err != nil {
panic("文件删除失败:" + err.Error())
return nil, false
}
}
file, err := os.OpenFile(filePath, os.O_CREATE|os.O_RDWR, 0666)
if err != nil {
panic("打开文件失败:" + err.Error())
return nil, false
}
return file, true
}
+35
View File
@@ -0,0 +1,35 @@
/*
* @Author : huangzj
* @Time : 2020/4/17 14:41
* @Description
*/
package util
import (
"gopkg.in/ini.v1"
"strconv"
)
func GenerateConfigBool(key string, real *bool, section *ini.Section) {
if section.Key(key).String() != "false" {
*real = true
return
}
*real = false
}
func GenerateConfig(key string, real string, section *ini.Section) {
if section.Key(key).String() != "" {
real = section.Key(key).String()
}
}
func GenerateConfigInt(key string, real int, section *ini.Section) {
if section.Key(key).String() != "" {
value, err := strconv.Atoi(section.Key(key).String())
if err == nil {
real = value
}
}
}