From e4e68d02bccb3df7fb77ae52f281a2c3eec7e3c7 Mon Sep 17 00:00:00 2001 From: "meilin.huang" <954537473@qq.com> Date: Mon, 11 Sep 2023 22:59:13 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=9C=BA=E5=99=A8=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E4=BC=98=E5=8C=96&sql=E6=96=B0=E5=A2=9E=E5=BC=95=E5=8F=B7?= =?UTF-8?q?=E5=8C=85=E8=A3=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/views/ops/db/component/DbTable.vue | 9 +- .../views/ops/db/component/tab/TableData.vue | 9 +- mayfly_go_web/src/views/ops/db/db.ts | 44 +++-- server/internal/machine/api/machine_file.go | 67 ++++---- .../machine/application/machine_file.go | 158 +++++++++--------- 5 files changed, 154 insertions(+), 133 deletions(-) diff --git a/mayfly_go_web/src/views/ops/db/component/DbTable.vue b/mayfly_go_web/src/views/ops/db/component/DbTable.vue index 6ea43b48..6cfae140 100644 --- a/mayfly_go_web/src/views/ops/db/component/DbTable.vue +++ b/mayfly_go_web/src/views/ops/db/component/DbTable.vue @@ -266,6 +266,7 @@ const cellClick = (row: any, column: any, cell: any) => { }; const submitUpdateFields = () => { + const dbInst = DbInst.getInst(state.dbId) let currentUpdatedFields = state.updatedFields; if (currentUpdatedFields.length <= 0) { return; @@ -274,12 +275,12 @@ const submitUpdateFields = () => { let res = ''; let divs: HTMLElement[] = []; currentUpdatedFields.forEach(a => { - let sql = `UPDATE ${state.table} SET `; + let sql = `UPDATE ${dbInst.wrapName(state.table)} SET `; let primaryKey = a.primaryKey; let primaryKeyType = a.primaryKeyType; let primaryKeyName = a.primaryKeyName; a.fields.forEach(f => { - sql += ` ${f.fieldName} = ${DbInst.wrapColumnValue(f.fieldType, f.newValue)},` + sql += ` ${dbInst.wrapName(f.fieldName)} = ${DbInst.wrapColumnValue(f.fieldType, f.newValue)},` // 如果修改的字段是主键 if (f.fieldName === primaryKeyName) { primaryKey = f.oldValue @@ -287,11 +288,11 @@ const submitUpdateFields = () => { divs.push(f.div) }) sql = sql.substring(0, sql.length - 1) - sql += ` WHERE ${primaryKeyName} = ${DbInst.wrapColumnValue(primaryKeyType, primaryKey)} ;` + sql += ` WHERE ${dbInst.wrapName(primaryKeyName)} = ${DbInst.wrapColumnValue(primaryKeyType, primaryKey)} ;` res += sql; }) - DbInst.getInst(state.dbId).promptExeSql(db, res, () => { }, () => { + dbInst.promptExeSql(db, res, () => { }, () => { currentUpdatedFields = []; divs.forEach(a => { a.classList.remove('update_field_active'); diff --git a/mayfly_go_web/src/views/ops/db/component/tab/TableData.vue b/mayfly_go_web/src/views/ops/db/component/tab/TableData.vue index 12da70e4..2d618f95 100644 --- a/mayfly_go_web/src/views/ops/db/component/tab/TableData.vue +++ b/mayfly_go_web/src/views/ops/db/component/tab/TableData.vue @@ -283,7 +283,7 @@ const selectData = async () => { const dbInst = state.ti.getNowDbInst(); const { db } = state.ti; try { - const countRes = await dbInst.runSql(db, DbInst.getDefaultCountSql(state.table, state.condition)); + const countRes = await dbInst.runSql(db, dbInst.getDefaultCountSql(state.table, state.condition)); state.count = countRes.res[0].count; let sql = dbInst.getDefaultSelectSql(state.table, state.condition, state.orderBy, state.pageNum, state.pageSize); state.sql = sql; @@ -427,6 +427,7 @@ const closeAddDataDialog = () => { const addRow = async () => { dataForm.value.validate(async (valid: boolean) => { if (valid) { + const dbInst = state.ti.getNowDbInst(); const data = state.addDataDialog.data; // key: 字段名,value: 字段名提示 let obj: any = {}; @@ -435,12 +436,12 @@ const addRow = async () => { if (!value) { continue; } - obj[`${item.columnName}`] = DbInst.wrapValueByType(value); + obj[`${dbInst.wrapName(item.columnName)}`] = DbInst.wrapValueByType(value); } let columnNames = Object.keys(obj).join(','); let values = Object.values(obj).join(','); - let sql = `INSERT INTO ${state.table} (${columnNames}) VALUES (${values});`; - state.ti.getNowDbInst().promptExeSql(state.ti.db, sql, null, () => { + let sql = `INSERT INTO ${dbInst.wrapName(state.table)} (${columnNames}) VALUES (${values});`; + dbInst.promptExeSql(state.ti.db, sql, null, () => { closeAddDataDialog(); onRefresh(); }); diff --git a/mayfly_go_web/src/views/ops/db/db.ts b/mayfly_go_web/src/views/ops/db/db.ts index 44884177..a0b7bc40 100644 --- a/mayfly_go_web/src/views/ops/db/db.ts +++ b/mayfly_go_web/src/views/ops/db/db.ts @@ -141,9 +141,19 @@ export class DbInst { }); } + /** + * 获取count sql + * @param table 表名 + * @param condition 条件 + * @returns count sql + */ + getDefaultCountSql = (table: string, condition?: string) => { + return `SELECT COUNT(*) count FROM ${this.wrapName(table)} ${condition ? 'WHERE ' + condition : ''} limit 1`; + }; + // 获取指定表的默认查询sql getDefaultSelectSql(table: string, condition: string, orderBy: string, pageNum: number, limit: number = DbInst.DefaultLimit) { - const baseSql = `SELECT * FROM ${table} ${condition ? 'WHERE ' + condition : ''} ${orderBy ? orderBy : ''}`; + const baseSql = `SELECT * FROM ${this.wrapName(table)} ${condition ? 'WHERE ' + condition : ''} ${orderBy ? orderBy : ''}`; if (this.type == 'mysql') { return `${baseSql} LIMIT ${(pageNum - 1) * limit}, ${limit};`; } @@ -170,10 +180,10 @@ export class DbInst { let values = []; for (let column of columns) { const colName = column.columnName; - colNames.push(colName); + colNames.push(this.wrapName(colName)); values.push(DbInst.wrapValueByType(data[colName])); } - sqls.push(`INSERT INTO ${table} (${colNames.join(', ')}) VALUES(${values.join(', ')})`); + sqls.push(`INSERT INTO ${this.wrapName(table)} (${colNames.join(', ')}) VALUES(${values.join(', ')})`); } return sqls.join(';\n') + ';'; } @@ -187,7 +197,7 @@ export class DbInst { const primaryKey = this.getDb(db).getColumn(table); const primaryKeyColumnName = primaryKey.columnName; const ids = datas.map((d: any) => `${DbInst.wrapColumnValue(primaryKey.columnType, d[primaryKeyColumnName])}`).join(','); - return `DELETE FROM ${table} WHERE ${primaryKeyColumnName} IN (${ids})`; + return `DELETE FROM ${this.wrapName(table)} WHERE ${this.wrapName(primaryKeyColumnName)} IN (${ids})`; } /* @@ -203,6 +213,22 @@ export class DbInst { }); }; + /** + * 包裹数据库表名、字段名等,避免使用关键字为字段名或表名时报错 + * @param table + * @param condition + * @returns + */ + wrapName = (name: string) => { + if (this.type == 'mysql') { + return `\`${name}\``; + } + if (this.type == 'postgres') { + return `"${name}"`; + } + return name; + }; + /** * 获取或新建dbInst,如果缓存中不存在则新建,否则直接返回 * @param inst 数据库实例,后端返回的列表接口中的信息 @@ -252,16 +278,6 @@ export class DbInst { dbInstCache.clear(); } - /** - * 获取count sql - * @param table 表名 - * @param condition 条件 - * @returns count sql - */ - static getDefaultCountSql = (table: string, condition?: string) => { - return `SELECT COUNT(*) count FROM ${table} ${condition ? 'WHERE ' + condition : ''}`; - }; - /** * 根据返回值包装值,若值为字符串类型则添加'' * @param val 值 diff --git a/server/internal/machine/api/machine_file.go b/server/internal/machine/api/machine_file.go index aeca4953..aacc336c 100644 --- a/server/internal/machine/api/machine_file.go +++ b/server/internal/machine/api/machine_file.go @@ -8,6 +8,7 @@ import ( "mayfly-go/internal/machine/api/vo" "mayfly-go/internal/machine/application" "mayfly-go/internal/machine/domain/entity" + "mayfly-go/internal/machine/infrastructure/machine" msgapp "mayfly-go/internal/msg/application" "mayfly-go/pkg/biz" "mayfly-go/pkg/ginx" @@ -67,18 +68,19 @@ func (m *MachineFile) CreateFile(rc *req.Ctx) { form := ginx.BindJsonAndValid(g, new(form.MachineCreateFileForm)) path := form.Path - mi := m.MachineFileApp.GetMachine(fid) - - attrs := jsonx.Kvs("machine", mi, "path", path) - rc.ReqParam = attrs - + attrs := jsonx.Kvs("path", path) + var mi *machine.Info + var err error if form.Type == dir { attrs["type"] = "目录" - m.MachineFileApp.MkDir(fid, form.Path) + mi, err = m.MachineFileApp.MkDir(fid, form.Path) } else { attrs["type"] = "文件" - m.MachineFileApp.CreateFile(fid, form.Path) + mi, err = m.MachineFileApp.CreateFile(fid, form.Path) } + attrs["machine"] = mi + rc.ReqParam = attrs + biz.ErrIsNilAppendErr(err, "创建目录失败: %s") } func (m *MachineFile) ReadFileContent(rc *req.Ctx) { @@ -87,7 +89,9 @@ func (m *MachineFile) ReadFileContent(rc *req.Ctx) { readPath := g.Query("path") readType := g.Query("type") - sftpFile := m.MachineFileApp.ReadFile(fid, readPath) + sftpFile, mi, err := m.MachineFileApp.ReadFile(fid, readPath) + rc.ReqParam = jsonx.Kvs("machine", mi, "path", readPath) + biz.ErrIsNilAppendErr(err, "打开文件失败: %s") defer sftpFile.Close() fileInfo, _ := sftpFile.Stat() @@ -96,9 +100,6 @@ func (m *MachineFile) ReadFileContent(rc *req.Ctx) { if readType != "1" { biz.IsTrue(filesize < max_read_size, "文件超过1m,请使用下载查看") } - - mi := m.MachineFileApp.GetMachine(fid) - rc.ReqParam = jsonx.Kvs("machine", mi, "path", readPath, "filesize", filesize) // 如果读取类型为下载,则下载文件,否则获取文件内容 if readType == "1" { // 截取文件名,如/usr/local/test.java -》 test.java @@ -161,10 +162,9 @@ func (m *MachineFile) WriteFileContent(rc *req.Ctx) { ginx.BindJsonAndValid(g, form) path := form.Path - mi := m.MachineFileApp.GetMachine(fid) + mi, err := m.MachineFileApp.WriteFileContent(fid, path, []byte(form.Content)) rc.ReqParam = jsonx.Kvs("machine", mi, "path", path) - - m.MachineFileApp.WriteFileContent(fid, path, []byte(form.Content)) + biz.ErrIsNilAppendErr(err, "打开文件失败: %s") } const MaxUploadFileSize int64 = 1024 * 1024 * 1024 @@ -181,9 +181,6 @@ func (m *MachineFile) UploadFile(rc *req.Ctx) { file, _ := fileheader.Open() defer file.Close() - mi := m.MachineFileApp.GetMachine(fid) - rc.ReqParam = jsonx.Kvs("machine", mi, "path", fmt.Sprintf("%s/%s", path, fileheader.Filename)) - la := rc.LoginAccount defer func() { if err := recover(); err != nil { @@ -195,7 +192,9 @@ func (m *MachineFile) UploadFile(rc *req.Ctx) { } }() - m.MachineFileApp.UploadFile(fid, path, fileheader.Filename, file) + mi, err := m.MachineFileApp.UploadFile(fid, path, fileheader.Filename, file) + rc.ReqParam = jsonx.Kvs("machine", mi, "path", fmt.Sprintf("%s/%s", path, fileheader.Filename)) + biz.ErrIsNilAppendErr(err, "创建文件失败: %s") // 保存消息并发送文件上传成功通知 m.MsgApp.CreateAndSend(la, ws.SuccessMsg("文件上传成功", fmt.Sprintf("[%s]文件已成功上传至 %s[%s:%s]", fileheader.Filename, mi.Name, mi.Ip, path))) } @@ -224,7 +223,9 @@ func (m *MachineFile) UploadFolder(rc *req.Ctx) { paths := mf.Value["paths"] folderName := filepath.Dir(paths[0]) - mi := m.MachineFileApp.GetMachine(fid) + mcli := m.MachineFileApp.GetMachineCli(fid, basePath+"/"+folderName) + mi := mcli.GetMachine() + sftpCli := mcli.GetSftpCli() rc.ReqParam = jsonx.Kvs("machine", mi, "path", fmt.Sprintf("%s/%s", basePath, folderName)) folderFiles := make([]FolderFile, len(paths)) @@ -234,7 +235,7 @@ func (m *MachineFile) UploadFolder(rc *req.Ctx) { dir := filepath.Dir(path) // 目录已建,则无需重复建 if !mkdirs[dir] { - m.MachineFileApp.MkDir(fid, basePath+"/"+dir) + biz.ErrIsNilAppendErr(sftpCli.MkdirAll(basePath+"/"+dir), "创建目录失败: %s") mkdirs[dir] = true } folderFiles[i] = FolderFile{ @@ -244,7 +245,7 @@ func (m *MachineFile) UploadFolder(rc *req.Ctx) { } // 分组处理 - groupNum := 10 + groupNum := 30 chunks := collx.ArraySplit(folderFiles, groupNum) var wg sync.WaitGroup @@ -271,15 +272,19 @@ func (m *MachineFile) UploadFolder(rc *req.Ctx) { dir := file.Dir file, _ := fileHeader.Open() defer file.Close() + logx.Debugf("上传文件夹: dir=%s -> filename=%s", dir, fileHeader.Filename) - m.MachineFileApp.UploadFile(fid, basePath+"/"+dir, fileHeader.Filename, file) + + createfile, err := sftpCli.Create(fmt.Sprintf("%s/%s/%s", basePath, dir, fileHeader.Filename)) + biz.ErrIsNilAppendErr(err, "创建文件失败: %s") + defer createfile.Close() + io.Copy(createfile, file) } }(chunk, &wg) } // 等待所有协程执行完成 wg.Wait() - // 保存消息并发送文件上传成功通知 m.MsgApp.CreateAndSend(rc.LoginAccount, ws.SuccessMsg("文件上传成功", fmt.Sprintf("[%s]文件夹已成功上传至 %s[%s:%s]", folderName, mi.Name, mi.Ip, basePath))) } @@ -291,10 +296,9 @@ func (m *MachineFile) RemoveFile(rc *req.Ctx) { rmForm := new(form.MachineFileOpForm) ginx.BindJsonAndValid(g, rmForm) - mi := m.MachineFileApp.GetMachine(fid) + mi, err := m.MachineFileApp.RemoveFile(fid, rmForm.Path...) rc.ReqParam = jsonx.Kvs("machine", mi, "path", rmForm.Path) - - m.MachineFileApp.RemoveFile(fid, rmForm.Path...) + biz.ErrIsNilAppendErr(err, "删除文件失败: %s") } func (m *MachineFile) CopyFile(rc *req.Ctx) { @@ -303,7 +307,8 @@ func (m *MachineFile) CopyFile(rc *req.Ctx) { cpForm := new(form.MachineFileOpForm) ginx.BindJsonAndValid(g, cpForm) - mi := m.MachineFileApp.Copy(fid, cpForm.ToPath, cpForm.Path...) + mi, err := m.MachineFileApp.Copy(fid, cpForm.ToPath, cpForm.Path...) + biz.ErrIsNilAppendErr(err, "文件拷贝失败: %s") rc.ReqParam = jsonx.Kvs("machine", mi, "cp", cpForm) } @@ -313,8 +318,9 @@ func (m *MachineFile) MvFile(rc *req.Ctx) { cpForm := new(form.MachineFileOpForm) ginx.BindJsonAndValid(g, cpForm) - mi := m.MachineFileApp.Mv(fid, cpForm.ToPath, cpForm.Path...) + mi, err := m.MachineFileApp.Mv(fid, cpForm.ToPath, cpForm.Path...) rc.ReqParam = jsonx.Kvs("machine", mi, "mv", cpForm) + biz.ErrIsNilAppendErr(err, "文件移动失败: %s") } func (m *MachineFile) Rename(rc *req.Ctx) { @@ -323,10 +329,9 @@ func (m *MachineFile) Rename(rc *req.Ctx) { rename := new(form.MachineFileRename) ginx.BindJsonAndValid(g, rename) - biz.ErrIsNilAppendErr(m.MachineFileApp.Rename(fid, rename.Oldname, rename.Newname), "文件重命名失败: %s") - - mi := m.MachineFileApp.GetMachine(fid) + mi, err := m.MachineFileApp.Rename(fid, rename.Oldname, rename.Newname) rc.ReqParam = jsonx.Kvs("machine", mi, "rename", rename) + biz.ErrIsNilAppendErr(err, "文件重命名失败: %s") } func getFileType(fm fs.FileMode) string { diff --git a/server/internal/machine/application/machine_file.go b/server/internal/machine/application/machine_file.go index 6194cf65..142bb8a2 100644 --- a/server/internal/machine/application/machine_file.go +++ b/server/internal/machine/application/machine_file.go @@ -1,6 +1,7 @@ package application import ( + "errors" "fmt" "io" "io/fs" @@ -31,15 +32,18 @@ type MachineFile interface { Delete(id uint64) // 获取文件关联的机器信息,主要用于记录日志使用 - GetMachine(fileId uint64) *machine.Info + // GetMachine(fileId uint64) *machine.Info + + // 检查文件路径,并返回机器id + GetMachineCli(fileId uint64, path ...string) *machine.Cli /** sftp 相关操作 **/ // 创建目录 - MkDir(fid uint64, path string) + MkDir(fid uint64, path string) (*machine.Info, error) // 创建文件 - CreateFile(fid uint64, path string) + CreateFile(fid uint64, path string) (*machine.Info, error) // 读取目录 ReadDir(fid uint64, path string) []fs.FileInfo @@ -51,22 +55,22 @@ type MachineFile interface { FileStat(fid uint64, path string) string // 读取文件内容 - ReadFile(fileId uint64, path string) *sftp.File + ReadFile(fileId uint64, path string) (*sftp.File, *machine.Info, error) // 写文件 - WriteFileContent(fileId uint64, path string, content []byte) + WriteFileContent(fileId uint64, path string, content []byte) (*machine.Info, error) // 文件上传 - UploadFile(fileId uint64, path, filename string, reader io.Reader) + UploadFile(fileId uint64, path, filename string, reader io.Reader) (*machine.Info, error) // 移除文件 - RemoveFile(fileId uint64, path ...string) + RemoveFile(fileId uint64, path ...string) (*machine.Info, error) - Copy(fileId uint64, toPath string, paths ...string) *machine.Info + Copy(fileId uint64, toPath string, paths ...string) (*machine.Info, error) - Mv(fileId uint64, toPath string, paths ...string) *machine.Info + Mv(fileId uint64, toPath string, paths ...string) (*machine.Info, error) - Rename(fileId uint64, oldname string, newname string) error + Rename(fileId uint64, oldname string, newname string) (*machine.Info, error) } func newMachineFileApp(machineFileRepo repository.MachineFile, machineRepo repository.Machine) MachineFile { @@ -111,20 +115,19 @@ func (m *machineFileAppImpl) Delete(id uint64) { } func (m *machineFileAppImpl) ReadDir(fid uint64, path string) []fs.FileInfo { - machineId := m.checkAndReturnMid(fid, path) + mcli := m.GetMachineCli(fid, path) if !strings.HasSuffix(path, "/") { path = path + "/" } - sftpCli := m.getSftpCli(machineId) - fis, err := sftpCli.ReadDir(path) + fis, err := mcli.GetSftpCli().ReadDir(path) biz.ErrIsNilAppendErr(err, "读取目录失败: %s") return fis } func (m *machineFileAppImpl) GetDirSize(fid uint64, path string) string { - machineId := m.checkAndReturnMid(fid, path) - res, err := GetMachineApp().GetCli(machineId).Run(fmt.Sprintf("du -sh %s", path)) + mcli := m.GetMachineCli(fid, path) + res, err := mcli.Run(fmt.Sprintf("du -sh %s", path)) if err != nil { // 若存在目录为空,则可能会返回如下内容。最后一行即为真正目录内容所占磁盘空间大小 //du: cannot access ‘/proc/19087/fd/3’: No such file or directory\n @@ -145,121 +148,116 @@ func (m *machineFileAppImpl) GetDirSize(fid uint64, path string) string { } func (m *machineFileAppImpl) FileStat(fid uint64, path string) string { - machineId := m.checkAndReturnMid(fid, path) - res, err := GetMachineApp().GetCli(machineId).Run(fmt.Sprintf("stat -L %s", path)) + mcli := m.GetMachineCli(fid, path) + res, err := mcli.Run(fmt.Sprintf("stat -L %s", path)) biz.ErrIsNil(err, res) return res } -func (m *machineFileAppImpl) MkDir(fid uint64, path string) { - machineId := m.checkAndReturnMid(fid, path) +func (m *machineFileAppImpl) MkDir(fid uint64, path string) (*machine.Info, error) { + mcli := m.GetMachineCli(fid, path) if !strings.HasSuffix(path, "/") { path = path + "/" } - sftpCli := m.getSftpCli(machineId) - err := sftpCli.MkdirAll(path) - biz.ErrIsNilAppendErr(err, "创建目录失败: %s") + err := mcli.GetSftpCli().MkdirAll(path) + return mcli.GetMachine(), err } -func (m *machineFileAppImpl) CreateFile(fid uint64, path string) { - machineId := m.checkAndReturnMid(fid, path) - sftpCli := m.getSftpCli(machineId) - file, err := sftpCli.Create(path) +func (m *machineFileAppImpl) CreateFile(fid uint64, path string) (*machine.Info, error) { + mcli := m.GetMachineCli(fid, path) + file, err := mcli.GetSftpCli().Create(path) biz.ErrIsNilAppendErr(err, "创建文件失败: %s") defer file.Close() + return mcli.GetMachine(), err } -func (m *machineFileAppImpl) ReadFile(fileId uint64, path string) *sftp.File { - machineId := m.checkAndReturnMid(fileId, path) - sftpCli := m.getSftpCli(machineId) +func (m *machineFileAppImpl) ReadFile(fileId uint64, path string) (*sftp.File, *machine.Info, error) { + mcli := m.GetMachineCli(fileId, path) // 读取文件内容 - fc, err := sftpCli.Open(path) - biz.ErrIsNilAppendErr(err, "打开文件失败: %s") - return fc + fc, err := mcli.GetSftpCli().Open(path) + return fc, mcli.GetMachine(), err } // 写文件内容 -func (m *machineFileAppImpl) WriteFileContent(fileId uint64, path string, content []byte) { - machineId := m.checkAndReturnMid(fileId, path) - - sftpCli := m.getSftpCli(machineId) - f, err := sftpCli.OpenFile(path, os.O_WRONLY|os.O_TRUNC|os.O_CREATE|os.O_RDWR) - biz.ErrIsNilAppendErr(err, "打开文件失败: %s") +func (m *machineFileAppImpl) WriteFileContent(fileId uint64, path string, content []byte) (*machine.Info, error) { + mcli := m.GetMachineCli(fileId, path) + mi := mcli.GetMachine() + f, err := mcli.GetSftpCli().OpenFile(path, os.O_WRONLY|os.O_TRUNC|os.O_CREATE|os.O_RDWR) + if err != nil { + return mi, err + } defer f.Close() - - fi, _ := f.Stat() - biz.IsTrue(!fi.IsDir(), "该路径不是文件") f.Write(content) + return mi, err } // 上传文件 -func (m *machineFileAppImpl) UploadFile(fileId uint64, path, filename string, reader io.Reader) { - machineId := m.checkAndReturnMid(fileId, path) +func (m *machineFileAppImpl) UploadFile(fileId uint64, path, filename string, reader io.Reader) (*machine.Info, error) { + mcli := m.GetMachineCli(fileId, path) + mi := mcli.GetMachine() if !strings.HasSuffix(path, "/") { path = path + "/" } - sftpCli := m.getSftpCli(machineId) - createfile, err := sftpCli.Create(path + filename) - biz.ErrIsNilAppendErr(err, "创建文件失败: %s") + createfile, err := mcli.GetSftpCli().Create(path + filename) + if err != nil { + return mi, err + } defer createfile.Close() - io.Copy(createfile, reader) + return mi, err } // 删除文件 -func (m *machineFileAppImpl) RemoveFile(fileId uint64, path ...string) { - machineId := m.checkAndReturnMid(fileId, path...) +func (m *machineFileAppImpl) RemoveFile(fileId uint64, path ...string) (*machine.Info, error) { + mcli := m.GetMachineCli(fileId, path...) + minfo := mcli.GetMachine() // 优先使用命令删除(速度快),sftp需要递归遍历删除子文件等 - mcli := GetMachineApp().GetCli(machineId) res, err := mcli.Run(fmt.Sprintf("rm -rf %s", strings.Join(path, " "))) if err == nil { - return + return minfo, nil } logx.Errorf("使用命令rm删除文件失败: %s", res) - sftpCli := m.getSftpCli(machineId) + sftpCli := mcli.GetSftpCli() for _, p := range path { - err := sftpCli.RemoveAll(p) - biz.ErrIsNilAppendErr(err, "删除文件失败: %s") + err = sftpCli.RemoveAll(p) + if err != nil { + break + } } + return minfo, err } -func (m *machineFileAppImpl) Copy(fileId uint64, toPath string, paths ...string) *machine.Info { - mid := m.checkAndReturnMid(fileId, paths...) - mcli := GetMachineApp().GetCli(mid) +func (m *machineFileAppImpl) Copy(fileId uint64, toPath string, paths ...string) (*machine.Info, error) { + mcli := m.GetMachineCli(fileId, paths...) + mi := mcli.GetMachine() res, err := mcli.Run(fmt.Sprintf("cp -r %s %s", strings.Join(paths, " "), toPath)) - biz.ErrIsNil(err, "文件拷贝失败: %s", res) - return mcli.GetMachine() + if err != nil { + return mi, errors.New(res) + } + return mi, err } -func (m *machineFileAppImpl) Mv(fileId uint64, toPath string, paths ...string) *machine.Info { - mid := m.checkAndReturnMid(fileId, paths...) - mcli := GetMachineApp().GetCli(mid) +func (m *machineFileAppImpl) Mv(fileId uint64, toPath string, paths ...string) (*machine.Info, error) { + mcli := m.GetMachineCli(fileId, paths...) + mi := mcli.GetMachine() res, err := mcli.Run(fmt.Sprintf("mv %s %s", strings.Join(paths, " "), toPath)) - biz.ErrIsNil(err, "文件移动失败: %s", res) - return mcli.GetMachine() + if err != nil { + return mi, errors.New(res) + } + return mi, err } -func (m *machineFileAppImpl) Rename(fileId uint64, oldname string, newname string) error { - mid := m.checkAndReturnMid(fileId, newname) - sftpCli := m.getSftpCli(mid) - return sftpCli.Rename(oldname, newname) +func (m *machineFileAppImpl) Rename(fileId uint64, oldname string, newname string) (*machine.Info, error) { + mcli := m.GetMachineCli(fileId, newname) + return mcli.GetMachine(), mcli.GetSftpCli().Rename(oldname, newname) } -// 获取sftp client -func (m *machineFileAppImpl) getSftpCli(machineId uint64) *sftp.Client { - return GetMachineApp().GetCli(machineId).GetSftpCli() -} - -func (m *machineFileAppImpl) GetMachine(fileId uint64) *machine.Info { - return GetMachineApp().GetCli(m.GetById(fileId).MachineId).GetMachine() -} - -// 校验并返回实际可访问的文件path -func (m *machineFileAppImpl) checkAndReturnMid(fid uint64, inputPath ...string) uint64 { +// 获取文件机器cli +func (m *machineFileAppImpl) GetMachineCli(fid uint64, inputPath ...string) *machine.Cli { biz.IsTrue(fid != 0, "文件id不能为空") mf := m.GetById(fid) biz.NotNil(mf, "文件不存在") @@ -267,5 +265,5 @@ func (m *machineFileAppImpl) checkAndReturnMid(fid uint64, inputPath ...string) // 接口传入的地址需为配置路径的子路径 biz.IsTrue(strings.HasPrefix(path, mf.Path), "无权访问该目录或文件: %s", path) } - return mf.MachineId + return GetMachineApp().GetCli(mf.MachineId) }