This commit is contained in:
sjcsjc123 2023-07-21 11:33:36 +08:00
parent 09e8965f1a
commit b6ace815d2
10 changed files with 454 additions and 0 deletions

View File

@ -1,2 +1,15 @@
# luwak
思路:
用户创建一张表后将表名以及所有字段都上传到redis里在api—gateway去获取表的信息
luwak负责注册一系列http接口
SELECT/{table}/select/{field1}=value1?{field2}=value2
UPDATE/{table}/update/{field1}=value1?{field2}=value2
DELETE/{table}/delete/{field1}=value1?{field2}=value2
INSERT/{table}/insert/{field1}=value1?{field2}=value2
FAQ: 如何获取用户的MYSQL连接信息

24
cmd/luwak/main.go Normal file
View File

@ -0,0 +1,24 @@
package main
import (
"luwak/handler"
"net/http"
)
func main() {
handler.RegisterAll()
handlers := handler.GetHandlers()
for url, h := range handlers {
if h == nil {
continue
}
http.Handle(url, http.HandlerFunc(h))
}
http.Handle("/ping", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("pong"))
}))
err := http.ListenAndServe(":8080", nil)
if err != nil {
panic(err)
}
}

5
go.mod Normal file
View File

@ -0,0 +1,5 @@
module luwak
go 1.20
require github.com/go-sql-driver/mysql v1.7.1

2
go.sum Normal file
View File

@ -0,0 +1,2 @@
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=

353
handler/handler.go Normal file
View File

@ -0,0 +1,353 @@
package handler
import (
"database/sql"
"encoding/json"
"fmt"
_ "github.com/go-sql-driver/mysql"
"luwak/model"
"net/http"
)
// 存储所有的handler函数
var handlers map[string]func(http.ResponseWriter, *http.Request)
// 存储所有的数据库连接
var connection map[string]*sql.DB
func GetTableAndColumns() []model.Table {
var rows *sql.Rows
tables := make(map[string]map[string][]string, 0)
defer func() {
if rows == nil {
return
}
if err := rows.Close(); err != nil {
panic(err)
}
}()
for dbName, db := range connection {
tables[dbName] = make(map[string][]string, 0)
var err error
var query string
// 查询数据库下的所有表名和字段名
query = "SELECT table_name, column_name FROM information_schema.columns WHERE table_schema = ?"
rows, err = db.Query(query, dbName)
if err != nil {
panic(err)
}
for rows.Next() {
var tableName, fieldName string
err := rows.Scan(&tableName, &fieldName)
if err != nil {
panic(err)
}
tables[dbName][tableName] = append(tables[dbName][tableName], fieldName)
}
rows.Close()
}
return convertMapToTable(tables)
}
func convertMapToTable(table map[string]map[string][]string) []model.Table {
var tables []model.Table
for dbName, v := range table {
for tableName, columns := range v {
tables = append(tables, model.Table{
DbName: dbName,
TableName: tableName,
Columns: columns,
})
}
}
return tables
}
// RegisterAll 注册所有的handler函数和数据库连接
func RegisterAll() {
handlers = make(map[string]func(http.ResponseWriter, *http.Request))
connection = make(map[string]*sql.DB)
//模拟从redis获取表结构
tables := []model.Table{
{
TableName: "user",
Columns: []string{"id", "name", "age"},
},
}
for _, table := range tables {
db, err := sql.Open("mysql", "root:root@tcp(127.0.0.1:3306)/"+table.TableName)
if err != nil {
panic(err)
}
connection[table.TableName] = db
registerInsertHandler(table)
registerQueryHandler(table)
registerUpdateHandler(table)
registerDeleteHandler(table)
}
tableAndColumns := GetTableAndColumns()
fmt.Println(tableAndColumns)
}
// registerInsertHandler 注册一系列插入数据的处理函数
func registerInsertHandler(table model.Table) {
handlers["/insert/"+table.TableName] = func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
//获取请求体
body := r.Body
defer body.Close()
param := make([]map[string]interface{}, 0)
//解析请求体
err := json.NewDecoder(body).Decode(&param)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
//校验请求体与表结构是否匹配
for _, p := range param {
if !checkParam(p, table.Columns) {
w.WriteHeader(http.StatusBadRequest)
return
}
}
for _, p := range param {
//按照顺序构造column和value
columns := ""
values := ""
for field, value := range p {
columns += field + ","
values += fmt.Sprintf("'%v',", value)
}
columns = columns[:len(columns)-1]
values = values[:len(values)-1]
//构造sql语句
execSql := fmt.Sprintf("insert into %s (%s) values (%s)", table.TableName, columns, values)
//执行sql语句
_, err := connection[table.TableName].Exec(execSql)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
}
}
}
// registerQueryHandler 注册一系列查询数据的处理函数
func registerQueryHandler(table model.Table) {
handlers["/query/"+table.TableName] = func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
//获取请求体
body := r.Body
defer body.Close()
param := make(map[string]interface{})
//解析请求体
err := json.NewDecoder(body).Decode(&param)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
//校验请求体与表结构是否匹配
if !checkParam(param, table.Columns) {
w.WriteHeader(http.StatusBadRequest)
return
}
//按照顺序构造column和value
where := "where"
if len(param) == 0 {
where = ""
} else {
for field, value := range param {
where += fmt.Sprintf("%s = '%v' and", field, value)
}
//去掉最后一个and
where = where[:len(where)-3]
}
//查询数据
db := connection[table.TableName]
//拼接sql语句
querySql := fmt.Sprintf("select * from %s %s", table.TableName, where)
rows, err := db.Query(querySql)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
//返回数据
var result []map[string]string
for rows.Next() {
columns, err := rows.Columns()
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
values := make([]sql.RawBytes, len(columns))
scanArgs := make([]interface{}, len(values))
for i := range values {
scanArgs[i] = &values[i]
}
err = rows.Scan(scanArgs...)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
row := make(map[string]string)
for i, col := range values {
if col == nil {
row[columns[i]] = ""
} else {
row[columns[i]] = string(col)
}
}
result = append(result, row)
}
w.Header().Set("Content-Type", "application/json; charset=utf-8")
bytes, err := json.Marshal(result)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
_, err = w.Write(bytes)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
}
}
// registerUpdateHandler 注册一系列更新数据的处理函数
func registerUpdateHandler(table model.Table) {
handlers["/update/"+table.TableName] = func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
//获取请求体
body := r.Body
defer body.Close()
param := make(map[string]interface{})
//解析请求体
err := json.NewDecoder(body).Decode(&param)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
//校验请求体与表结构是否匹配
if !checkParam(param, table.Columns) {
w.WriteHeader(http.StatusBadRequest)
return
}
if param["where"] == nil {
w.WriteHeader(http.StatusBadRequest)
return
}
//按照顺序构造column和value
set := ""
for field, value := range param {
if field == "where" {
continue
}
set += fmt.Sprintf("%s = '%v',", field, value)
}
//去掉最后一个逗号
set = set[:len(set)-1]
//构造where
m, ok := param["where"].(map[string]interface{})
if !ok {
w.WriteHeader(http.StatusBadRequest)
return
}
where := "where "
for field, value := range m {
where += fmt.Sprintf("%s = '%v' and", field, value)
}
//去掉最后一个and
where = where[:len(where)-3]
//更新数据
db := connection[table.TableName]
//拼接sql语句
updateSql := fmt.Sprintf("update %s set %s %s", table.TableName, set, where)
_, err = db.Exec(updateSql)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
}
}
// registerDeleteHandler 注册一系列删除数据的处理函数
func registerDeleteHandler(table model.Table) {
handlers["/delete/"+table.TableName] = func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
//获取请求体
body := r.Body
defer body.Close()
param := make(map[string]interface{})
//解析请求体
err := json.NewDecoder(body).Decode(&param)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
//校验请求体与表结构是否匹配
if !checkParam(param, table.Columns) {
w.WriteHeader(http.StatusBadRequest)
return
}
//构造where
where := "where "
if len(param) == 0 {
w.WriteHeader(http.StatusBadRequest)
return
} else {
for field, value := range param {
where += fmt.Sprintf("%s = '%v' and", field, value)
}
//去掉最后一个and
where = where[:len(where)-3]
}
//删除数据
db := connection[table.TableName]
//拼接sql语句
deleteSql := fmt.Sprintf("delete from %s %s", table.TableName, where)
_, err = db.Exec(deleteSql)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
}
}
func GetHandlers() map[string]func(http.ResponseWriter, *http.Request) {
return handlers
}
func checkParam(param map[string]interface{}, cols []string) bool {
for k := range param {
//跳过where字段
if k == "where" || k == "limit" || k == "offset" || k == "order" || k == "having" || k == "group" {
continue
}
ok := false
for _, col := range cols {
if k == col {
ok = true
continue
}
}
if !ok {
return false
}
}
return true
}

10
model/table.go Normal file
View File

@ -0,0 +1,10 @@
package model
// Table 表结构
// TableName 表名
// Columns 列名
type Table struct {
DbName string `json:"db_name"`
TableName string `json:"table_name"`
Columns []string `json:"columns"`
}

6
test/delete.http Normal file
View File

@ -0,0 +1,6 @@
POST http://localhost:8080/delete/user
Content-Type: application/json
{
"name": "Test"
}

24
test/insert.http Normal file
View File

@ -0,0 +1,24 @@
### Request1
POST http://localhost:8080/test
Content-Type: application/json
[
{
"sql": "INSERT INTO user (name, age) VALUES (?, ?)",
"params": [
"John",
30
]
}
]
### Request2
POST http://localhost:8080/insert/user
Content-Type: application/json
{
"name": "John",
"age": 30
}

6
test/query.http Normal file
View File

@ -0,0 +1,6 @@
POST http://localhost:8080/query/user
Content-Type: application/json
{
}

11
test/update.http Normal file
View File

@ -0,0 +1,11 @@
POST http://localhost:8080/update/user
Content-Type: application/json
{
"name": "John1",
"age": 30,
"where": {
"name": "John",
"age": 30
}
}