Compare commits

...

3 Commits

Author SHA1 Message Date
junleea 29c05b0af7 支持保存顺序 2025-08-20 22:28:36 +08:00
junleea 96e1a96f2e 修复数据库接口问题 2025-08-20 20:59:34 +08:00
junleea 7844e5b121 添加数据库管理接口 2025-08-19 22:43:31 +08:00
7 changed files with 579 additions and 0 deletions

View File

@ -5,6 +5,7 @@ import (
"gorm.io/driver/mysql"
"gorm.io/driver/postgres"
"gorm.io/gorm"
"log"
"videoplayer/proto"
)
@ -104,6 +105,15 @@ func Init() error {
fmt.Println("shell table:", err)
}
err = db.AutoMigrate(&proto.DBManage{})
if err != nil {
log.Println("dbmanage table:", err)
}
err = db.AutoMigrate(&proto.SQLRunHistory{})
if err != nil {
log.Println("sqlrunhistory table:", err)
}
DB = db
return err
}

212
dao/dbm.go Normal file
View File

@ -0,0 +1,212 @@
package dao
import (
"gorm.io/gorm"
"videoplayer/proto"
)
func CreateDBManage(db_info proto.DBManage) (uint, error) {
var db2 *gorm.DB
if proto.Config.SERVER_SQL_LOG {
db2 = DB.Debug()
} else {
db2 = DB
}
res := db2.Create(&db_info)
if res.Error != nil {
return 0, res.Error
}
return db_info.ID, nil
}
func CreateDBRunHistory(history *proto.SQLRunHistory) (uint, error) {
var db2 *gorm.DB
if proto.Config.SERVER_SQL_LOG {
db2 = DB.Debug()
} else {
db2 = DB
}
res := db2.Create(history)
if res.Error != nil {
return 0, res.Error
}
return history.ID, nil
}
func RunSQL(sql string, db_ *gorm.DB) (res []map[string]interface{}, err error) {
var db2 *gorm.DB
if proto.Config.SERVER_SQL_LOG {
db2 = db_.Debug()
} else {
db2 = db_
}
err = db2.Raw(sql).Scan(&res).Error
return res, err
}
func FindDBManageByID(id uint) (proto.DBManage, error) {
var db_info proto.DBManage
var db2 *gorm.DB
if proto.Config.SERVER_SQL_LOG {
db2 = DB.Debug()
} else {
db2 = DB
}
res := db2.Where("id = ?", id).First(&db_info)
if res.Error != nil {
return proto.DBManage{}, res.Error
}
return db_info, nil
}
func FindDBManageByAuthID(auth_id uint) ([]proto.DBManage, error) {
var db_infos []proto.DBManage
var db2 *gorm.DB
if proto.Config.SERVER_SQL_LOG {
db2 = DB.Debug()
} else {
db2 = DB
}
res := db2.Where("user_id = ?", auth_id).Find(&db_infos)
if res.Error != nil {
return nil, res.Error
}
return db_infos, nil
}
func FindAllDBManage() ([]proto.DBManage, error) {
var db_infos []proto.DBManage
var db2 *gorm.DB
if proto.Config.SERVER_SQL_LOG {
db2 = DB.Debug()
} else {
db2 = DB
}
res := db2.Find(&db_infos)
if res.Error != nil {
return nil, res.Error
}
return db_infos, nil
}
func UpdateDBManage(id uint, db_info *proto.DBManage) error {
var db2 *gorm.DB
if proto.Config.SERVER_SQL_LOG {
db2 = DB.Debug()
} else {
db2 = DB
}
res := db2.Model(&proto.DBManage{}).Where("id = ?", id).Updates(db_info)
return res.Error
}
func DeleteDBManageByID(id uint) error {
var db2 *gorm.DB
if proto.Config.SERVER_SQL_LOG {
db2 = DB.Debug()
} else {
db2 = DB
}
res := db2.Where("id = ?", id).Delete(&proto.DBManage{})
return res.Error
}
func FindDBRunHistoryByID(id uint) (proto.SQLRunHistory, error) {
var history proto.SQLRunHistory
var db2 *gorm.DB
if proto.Config.SERVER_SQL_LOG {
db2 = DB.Debug()
} else {
db2 = DB
}
res := db2.Where("id = ?", id).First(&history)
if res.Error != nil {
return proto.SQLRunHistory{}, res.Error
}
return history, nil
}
func FindDBRunHistoryByAuthID(auth_id int) ([]proto.SQLRunHistory, error) {
var histories []proto.SQLRunHistory
var db2 *gorm.DB
if proto.Config.SERVER_SQL_LOG {
db2 = DB.Debug()
} else {
db2 = DB
}
res := db2.Where("user_id = ?", auth_id).Find(&histories)
if res.Error != nil {
return nil, res.Error
}
return histories, nil
}
func FindAllSQLRunHistory() ([]proto.SQLRunHistory, error) {
var histories []proto.SQLRunHistory
var db2 *gorm.DB
if proto.Config.SERVER_SQL_LOG {
db2 = DB.Debug()
} else {
db2 = DB
}
res := db2.Find(&histories)
if res.Error != nil {
return nil, res.Error
}
return histories, nil
}
func RunSQLWithOrder(sql string, db_ *gorm.DB) (result proto.SQLResult, err error) {
var db2 *gorm.DB
// 保留 Debug 模式
if proto.Config.SERVER_SQL_LOG {
db2 = db_.Debug()
} else {
db2 = db_
}
// 执行 SQL 并获取底层 Rows 对象
rows, err := db2.Raw(sql).Rows()
if err != nil {
return result, err
}
defer rows.Close() // 确保关闭 Rows
// 获取列名顺序(关键:这里的顺序与 SQL 查询的列顺序一致)
columns, err := rows.Columns()
if err != nil {
return result, err
}
result.Columns = columns // 保存列名顺序
// 遍历每行数据
for rows.Next() {
// 准备接收每行数据的容器(按列顺序)
values := make([]interface{}, len(columns))
valuePtrs := make([]interface{}, len(columns)) // 用于 Scan 的指针切片
// 为每个列绑定指针Scan 要求传入指针)
for i := range values {
valuePtrs[i] = &values[i]
}
// 扫描当前行数据到指针切片
if err2 := rows.Scan(valuePtrs...); err2 != nil {
return result, err2
}
// 将当前行数据存入 map便于按列名访问
rowMap := make(map[string]interface{})
for i, col := range columns {
rowMap[col] = values[i]
}
result.Rows = append(result.Rows, rowMap)
}
// 检查遍历过程中是否有错误
if err = rows.Err(); err != nil {
return result, err
}
return result, nil
}

133
handler/dbm.go Normal file
View File

@ -0,0 +1,133 @@
package handler
import (
"github.com/gin-gonic/gin"
"net/http"
"videoplayer/proto"
"videoplayer/service"
)
func SetDBManageGroup(router *gin.Engine) {
dbm := router.Group("/dbm")
dbm.POST("/run_sql", RunSQLHandler) // 运行SQL语句
dbm.POST("/create_db_manage", CreateDBManageHandler) // 创建数据库管理
dbm.POST("/get_db_manage", GetDBManageHandler) // 获取数据库管理信息
dbm.POST("/get_sql_history", GetSQLRunHistoryHandler) // 获取SQL运行历史
dbm.POST("/update_db_manage", UpdateDBManageHandler) // 更新数据库管理信息
}
func UpdateDBManageHandler(c *gin.Context) {
id, _ := c.Get("id")
userID := int(id.(float64))
var req proto.UpdateDBManageReq
var resp proto.GeneralResp
if err := c.ShouldBind(&req); err != nil {
resp.Code = proto.ParameterError
resp.Message = "请求参数解析错误"
} else {
dbManage, err2 := service.UpdateDBManage(&req, userID)
if err2 != nil {
resp.Code = proto.DBMUpdateFailed
resp.Message = "更新数据库管理失败: " + err2.Error()
} else {
resp.Code = proto.SuccessCode
resp.Message = "更新数据库管理成功"
resp.Data = dbManage
}
}
c.JSON(http.StatusOK, resp)
}
func GetSQLRunHistoryHandler(c *gin.Context) {
id, _ := c.Get("id")
userID := int(id.(float64))
var req proto.GetSQLRunHistoryReq
var resp proto.GeneralResp
if err := c.ShouldBind(&req); err != nil {
resp.Code = proto.ParameterError
resp.Message = "请求参数解析错误"
} else {
history, err2 := service.GetSQLRunHistory(&req, userID)
if err2 != nil {
resp.Code = proto.DBMGetFailed
resp.Message = "获取SQL运行历史失败: " + err2.Error()
} else {
resp.Code = proto.SuccessCode
resp.Message = "获取SQL运行历史成功"
resp.Data = history
}
}
c.JSON(http.StatusOK, resp)
}
func GetDBManageHandler(c *gin.Context) {
id, _ := c.Get("id")
userID := uint(id.(float64))
var req proto.GetDBManageReq
var resp proto.GeneralResp
if err := c.ShouldBind(&req); err != nil {
resp.Code = proto.ParameterError
resp.Message = "请求参数解析错误"
} else {
dbManage, err2 := service.GetDBManageList(&req, userID)
if err2 != nil {
resp.Code = proto.DBMGetFailed
resp.Message = "获取数据库管理信息失败: " + err2.Error()
} else {
resp.Code = proto.SuccessCode
resp.Message = "获取数据库管理信息成功"
resp.Data = dbManage
}
}
c.JSON(http.StatusOK, resp)
}
func CreateDBManageHandler(c *gin.Context) {
id, _ := c.Get("id")
userID := uint(id.(float64))
var req proto.CreateDBManageReq
var resp proto.GeneralResp
if err := c.ShouldBind(&req); err != nil {
resp.Code = proto.ParameterError
resp.Message = "请求参数解析错误"
} else {
dbManage, err2 := service.CreateDBManage(&req, userID)
if err2 != nil {
resp.Code = proto.DBMCreateFailed
resp.Message = "创建数据库管理失败: " + err.Error()
} else {
resp.Code = proto.SuccessCode
resp.Message = "创建数据库管理成功"
resp.Data = dbManage
}
}
c.JSON(http.StatusOK, resp)
}
func RunSQLHandler(c *gin.Context) {
id, _ := c.Get("id")
userID := uint(id.(float64))
// 处理运行SQL请求
var req proto.RunSQLRequest
var resp proto.GeneralResp
if err := c.ShouldBind(&req); err != nil {
resp.Code = proto.ParameterError
resp.Message = "请求参数解析错误"
} else {
req.UserID = userID
res, err2 := service.RunSQL(&req)
if err2 != nil {
resp.Code = proto.DBMRunSQLFailed
resp.Message = "运行SQL失败: " + err2.Error()
} else {
resp.Code = proto.SuccessCode
resp.Message = "运行SQL成功"
resp.Data = res
}
}
c.JSON(http.StatusOK, resp)
}

View File

@ -46,6 +46,7 @@ func main() {
handler.SetUpToolGroup(r) // Tool
handler.SetUpFileGroup(r) // File
handler.SetUpShellGroup(r) // Shell
handler.SetDBManageGroup(r) // DBM
defer dao.Close()
defer worker.CloseRedis()
//定时任务

75
proto/dbm.go Normal file
View File

@ -0,0 +1,75 @@
package proto
import "gorm.io/gorm"
const (
DB_TYPE_MYSQL = 0 // DBTypeMySQL MySQL数据库
DB_TYPE_POSTGRES = 1 // DBTypePostgres PostgreSQL数据库
DB_TYPE_SQLITE = 2 // DBTypeSQLite SQLite数据库
DB_TYPE_SQLSERVER = 3 // DBTypeSQLServer SQL Server数据库
DB_TYPE_ORACLE = 4 // DBTypeOracle Oracle数据库
DB_TYPE_MONGODB = 5 // DBTypeMongoDB MongoDB数据库
DB_TYPE_REDIS = 6 // DBTypeRedis Redis数据库
)
type RunSQLRequest struct {
SQL string `json:"sql" form:"sql"` // SQL语句
DB_ID uint `json:"db_id" form:"db_id"` // 数据库ID
UserID uint `json:"user_id" form:"user_id"` // 用户ID
}
type DBManage struct {
gorm.Model
UserID uint `gorm:"column:user_id"` // 用户ID
DB_IP string `gorm:"column:db_ip;type:varchar(255);uniqueIndex:idx_db_ip"` // 数据库IP
DB_Port uint `gorm:"column:db_port"` // 数据库端口
DB_NAME string `gorm:"column:db_name;type:varchar(255);uniqueIndex:idx_db_name"` // 数据库名称
DB_User string `gorm:"column:db_user;type:varchar(255);uniqueIndex:idx_db_user"` // 数据库用户名
DB_Password string `gorm:"column:db_password;type:varchar(255);uniqueIndex:idx_db_password"` // 数据库密码
DB_Type uint `gorm:"column:db_type"` // 数据库类型: 0为mysql,1为postgres,2为sqlite,3为sqlserver,4为oracle,5为mongodb,6为redis
DB_Desc string `gorm:"column:db_desc;type:varchar(255)"` // 数据库描述
DB_STATUS uint `gorm:"column:db_status"` // 数据库状态: 0为未连接,1为已连接,2为连接失败
}
type SQLRunHistory struct {
gorm.Model
UserID uint `gorm:"column:user_id"` // 用户ID
SQL string `gorm:"column:sql;type:text"` // 执行的SQL语句
DB_ID uint `gorm:"column:db_id"` // 数据库ID
Status uint `gorm:"column:status"` // 执行状态: 0为成功,1为失败
}
type CreateDBManageReq struct {
DB_IP string `json:"db_ip" form:"db_ip"` // 数据库IP
DB_Port uint `json:"db_port" form:"db_port"` // 数据库端口
DB_NAME string `json:"db_name" form:"db_name"` // 数据库名称
DB_User string `json:"db_user" form:"db_user"` // 数据库用户名
DB_Password string `json:"db_password" form:"db_password"` // 数据库密码
DB_Type uint `json:"db_type" form:"db_type"` // 数据库类型: 0为mysql,1为postgres,2为sqlite,3为sqlserver,4为oracle,5为mongodb,6为redis
}
type UpdateDBManageReq struct {
DB_ID uint `json:"db_id" form:"db_id"` // 数据库ID
DB_IP string `json:"db_ip" form:"db_ip"` // 数据库IP
DB_Port uint `json:"db_port" form:"db_port"` // 数据库端口
DB_NAME string `json:"db_name" form:"db_name"` // 数据库名称
DB_User string `json:"db_user" form:"db_user"` // 数据库用户名
DB_Password string `json:"db_password" form:"db_password"` // 数据库密码
DB_Type uint `json:"db_type" form:"db_type"` // 数据库类型: 0为mysql,1为postgres,2为sqlite,3为sqlserver,4为oracle,5为mongodb,6为redis
}
type GetDBManageReq struct {
DB_ID uint `json:"db_id" form:"db_id"` // 数据库ID
GET_TYPE int `json:"get_type" form:"get_type"` // 获取类型: 0获取自己1为获取全部管理员权限
}
type GetSQLRunHistoryReq struct {
DB_ID uint `json:"db_id" form:"db_id"` // 数据库ID
GET_TYPE int `json:"get_type" form:"get_type"` // 获取类型: 0获取自己1为获取全部管理员权限
}
// SQLResult 包含查询结果的列名顺序和对应数据
type SQLResult struct {
Columns []string // 列名顺序(与 SQL 查询的列顺序一致)
Rows []map[string]interface{} // 每行数据map 便于按列名访问)
}

View File

@ -79,4 +79,10 @@ const (
MonitorServerIDNotFound = 111 // 监控服务器ID不存在
SigningKeyVersionIsTooOld = 200
//下面是数据库管理工具-错误状态码 100x
DBMRunSQLFailed = 1001 // 执行SQL失败
DBMCreateFailed = 1002 // 创建数据库管理失败
DBMGetFailed = 1003 // 获取数据库管理信息失败`
DBMUpdateFailed = 1004 // 更新数据库管理信息失败
)

142
service/dbmService.go Normal file
View File

@ -0,0 +1,142 @@
package service
import (
"errors"
"gorm.io/driver/mysql"
"gorm.io/driver/postgres"
"gorm.io/gorm"
"strconv"
"videoplayer/dao"
"videoplayer/proto"
)
func RunSQL(req *proto.RunSQLRequest) ([]map[string]interface{}, error) {
dbmInfo, err := dao.FindDBManageByID(req.DB_ID)
if err != nil {
return nil, err
}
if dbmInfo.UserID != req.UserID {
return nil, errors.New("unauthorized access to the database management system")
}
db_, err := GetGORMDBObject(&dbmInfo)
if err != nil {
return nil, err
}
res, err := dao.RunSQL(req.SQL, db_)
if err != nil {
return nil, err
}
// 记录执行历史
history := &proto.SQLRunHistory{UserID: req.UserID, DB_ID: req.DB_ID, SQL: req.SQL, Status: 0}
_, err = dao.CreateDBRunHistory(history)
if err != nil {
return nil, err
}
return res, nil
}
func GetGORMDBObject(dbmInfo *proto.DBManage) (db_ *gorm.DB, err error) {
switch dbmInfo.DB_Type {
case proto.DB_TYPE_MYSQL: // MySQL
dsn := dbmInfo.DB_User + ":" + dbmInfo.DB_Password + "@tcp(" + dbmInfo.DB_IP + ":" + strconv.Itoa(int(dbmInfo.DB_Port)) + ")/" + dbmInfo.DB_NAME + "?charset=utf8mb4&parseTime=True&loc=Local"
db_, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
return nil, err
}
case proto.DB_TYPE_POSTGRES: // PostgreSQL
dsn := "host=" + dbmInfo.DB_IP + " user=" + dbmInfo.DB_User + " password=" + dbmInfo.DB_Password + " dbname=" + dbmInfo.DB_NAME + " port=" + strconv.Itoa(int(dbmInfo.DB_Port)) + " sslmode=disable TimeZone=Asia/Shanghai"
db_, err = gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil {
return nil, err
}
default:
err = errors.New("unsupported database type")
}
return db_, err
}
func CreateDBManage(req *proto.CreateDBManageReq, userID uint) (proto.DBManage, error) {
dbmInfo := proto.DBManage{
UserID: userID,
DB_IP: req.DB_IP,
DB_Port: req.DB_Port,
DB_NAME: req.DB_NAME,
DB_User: req.DB_User,
DB_Password: req.DB_Password,
DB_Type: req.DB_Type,
DB_Desc: "",
DB_STATUS: 0, // 初始状态为未连接
}
id, err := dao.CreateDBManage(dbmInfo)
if err != nil {
return proto.DBManage{}, err
}
dbmInfo.ID = id
return dbmInfo, nil
}
func GetDBManageList(req *proto.GetDBManageReq, userID uint) ([]proto.DBManage, error) {
var dbmList []proto.DBManage
var err error
if req.GET_TYPE == 0 { // 获取自己的数据库管理
dbmList, err = dao.FindDBManageByAuthID(userID)
} else if req.GET_TYPE == 1 { // 管理员获取所有数据库管理
user := GetUserByIDFromUserCenter(int(userID))
if user.Role != "admin" {
return nil, errors.New("unauthorized access, only admin can get all database management")
}
dbmList, err = dao.FindAllDBManage()
} else {
return nil, errors.New("invalid get type")
}
if err != nil {
return nil, err
}
return dbmList, nil
}
func GetSQLRunHistory(req *proto.GetSQLRunHistoryReq, userID int) ([]proto.SQLRunHistory, error) {
var historyList []proto.SQLRunHistory
var err error
if req.GET_TYPE == 0 { // 获取自己的SQL执行历史
historyList, err = dao.FindDBRunHistoryByAuthID(userID)
} else if req.GET_TYPE == 1 { // 管理员获取所有SQL执行历史
user := GetUserByIDFromUserCenter(userID)
if user.Role != "admin" {
return nil, errors.New("unauthorized access, only admin can get all SQL run history")
}
historyList, err = dao.FindAllSQLRunHistory()
} else {
return nil, errors.New("invalid get type")
}
if err != nil {
return nil, err
}
return historyList, nil
}
func UpdateDBManage(req *proto.UpdateDBManageReq, userID int) (proto.DBManage, error) {
dbmInfo, err := dao.FindDBManageByID(req.DB_ID)
if err != nil {
return proto.DBManage{}, err
}
if dbmInfo.UserID != uint(userID) && GetUserByIDFromUserCenter(userID).Role != "admin" {
return proto.DBManage{}, errors.New("unauthorized access to the database management system")
}
dbmInfo.DB_IP = req.DB_IP
dbmInfo.DB_Port = req.DB_Port
dbmInfo.DB_NAME = req.DB_NAME
dbmInfo.DB_User = req.DB_User
dbmInfo.DB_Password = req.DB_Password
dbmInfo.DB_Type = req.DB_Type
err = dao.UpdateDBManage(dbmInfo.ID, &dbmInfo)
if err != nil {
return proto.DBManage{}, err
}
return dbmInfo, nil
}