add: list and download log&cdr export file
This commit is contained in:
@@ -667,6 +667,10 @@ INSERT INTO `sys_dict_data` VALUES (2156, 2156, 'job.ne_config_backup_remark', '
|
||||
INSERT INTO `sys_dict_data` VALUES (2157, 2157, 'job.exportOperateLog', '定期从操作日志表导出文件到指定目录', 'i18n_zh', '', '', '1', 'supervisor', 1721902269805, '', 0, '');
|
||||
INSERT INTO `sys_dict_data` VALUES (2158, 2158, 'job.exportIMSCDR', '定期从语音话单表导出文件至指定目录', 'i18n_zh', '', '', '1', 'supervisor', 1721902269805, '', 0, '');
|
||||
INSERT INTO `sys_dict_data` VALUES (2159, 2159, 'job.exportSMFCDR', '定期从数据话单表导出文件至指定目录', 'i18n_zh', '', '', '1', 'supervisor', 1721902269805, '', 0, '');
|
||||
INSERT INTO `sys_dict_data` VALUES (2160, 2160, 'table.sys_log_operate', '操作日志', 'i18n_zh', '', '', '1', 'supervisor', 1721902269805, '', 0, '');
|
||||
INSERT INTO `sys_dict_data` VALUES (2161, 2161, 'table.cdr_event_ims', '语音话单', 'i18n_zh', '', '', '1', 'supervisor', 1721902269805, '', 0, '');
|
||||
INSERT INTO `sys_dict_data` VALUES (2162, 2162, 'table.cdr_event_smf', '数据话单', 'i18n_zh', '', '', '1', 'supervisor', 1721902269805, '', 0, '');
|
||||
INSERT INTO `sys_dict_data` VALUES (2163, 2163, 'table.cdr_event_smsc', '短信话单', 'i18n_zh', '', '', '1', 'supervisor', 1721902269805, '', 0, '');
|
||||
|
||||
-- 多租户
|
||||
INSERT INTO `sys_dict_data` VALUES (11000, 11000, 'menu.security.tenant', '租户管理', 'i18n_zh', NULL, NULL, '1', 'supervisor', 1700000000000, NULL, 0, NULL);
|
||||
|
||||
@@ -667,6 +667,10 @@ INSERT INTO `sys_dict_data` VALUES (4156, 4156, 'job.ne_config_backup_remark', '
|
||||
INSERT INTO `sys_dict_data` VALUES (4157, 4157, 'job.exportOperateLog', 'Export regularly from operation log table', 'i18n_en', '', '', '1', 'supervisor', 1721902269805, '', 0, '');
|
||||
INSERT INTO `sys_dict_data` VALUES (4158, 4158, 'job.exportIMSCDR', 'Export regularly from IMS CDR table', 'i18n_en', '', '', '1', 'supervisor', 1721902269805, '', 0, '');
|
||||
INSERT INTO `sys_dict_data` VALUES (4159, 4159, 'job.exportSMFCDR', 'Export regularly from SMF CDR table', 'i18n_en', '', '', '1', 'supervisor', 1721902269805, '', 0, '');
|
||||
INSERT INTO `sys_dict_data` VALUES (4160, 4160, 'table.sys_log_operate', 'Operation Log', 'i18n_en', '', '', '1', 'supervisor', 1721902269805, '', 0, '');
|
||||
INSERT INTO `sys_dict_data` VALUES (4161, 4161, 'table.cdr_event_ims', 'Voice CDR', 'i18n_en', '', '', '1', 'supervisor', 1721902269805, '', 0, '');
|
||||
INSERT INTO `sys_dict_data` VALUES (4162, 4162, 'table.cdr_event_smf', 'Data CDR', 'i18n_en', '', '', '1', 'supervisor', 1721902269805, '', 0, '');
|
||||
INSERT INTO `sys_dict_data` VALUES (4163, 4163, 'table.cdr_event_smsc', 'SMS CDR', 'i18n_en', '', '', '1', 'supervisor', 1721902269805, '', 0, '');
|
||||
|
||||
-- 多租户
|
||||
INSERT INTO `sys_dict_data` VALUES (14000, 14000, 'menu.security.tenant', 'Tenant Management', 'i18n_en', '', '', '1', 'supervisor', 1705550000000, '', 0, '');
|
||||
|
||||
@@ -674,7 +674,10 @@ REPLACE INTO `sys_dict_data` VALUES (2156, 2156, 'job.ne_config_backup_remark',
|
||||
REPLACE INTO `sys_dict_data` VALUES (2157, 2157, 'job.exportOperateLog', '定期从操作日志表导出文件到指定目录', 'i18n_zh', '', '', '1', 'supervisor', 1721902269805, '', 0, '');
|
||||
REPLACE INTO `sys_dict_data` VALUES (2158, 2158, 'job.exportIMSCDR', '定期从语音话单表导出文件至指定目录', 'i18n_zh', '', '', '1', 'supervisor', 1721902269805, '', 0, '');
|
||||
REPLACE INTO `sys_dict_data` VALUES (2159, 2159, 'job.exportSMFCDR', '定期从数据话单表导出文件至指定目录', 'i18n_zh', '', '', '1', 'supervisor', 1721902269805, '', 0, '');
|
||||
|
||||
REPLACE INTO `sys_dict_data` VALUES (2160, 2160, 'table.sys_log_operate', '操作日志', 'i18n_zh', '', '', '1', 'supervisor', 1721902269805, '', 0, '');
|
||||
REPLACE INTO `sys_dict_data` VALUES (2161, 2161, 'table.cdr_event_ims', '语音话单', 'i18n_zh', '', '', '1', 'supervisor', 1721902269805, '', 0, '');
|
||||
REPLACE INTO `sys_dict_data` VALUES (2162, 2162, 'table.cdr_event_smf', '数据话单', 'i18n_zh', '', '', '1', 'supervisor', 1721902269805, '', 0, '');
|
||||
REPLACE INTO `sys_dict_data` VALUES (2163, 2163, 'table.cdr_event_smsc', '短信话单', 'i18n_zh', '', '', '1', 'supervisor', 1721902269805, '', 0, '');
|
||||
|
||||
-- multi-tenancy
|
||||
REPLACE INTO `sys_dict_data` VALUES (11000, 11000, 'menu.security.tenant', '租户管理', 'i18n_zh', NULL, NULL, '1', 'supervisor', 1700000000000, NULL, 0, NULL);
|
||||
|
||||
@@ -669,6 +669,10 @@ REPLACE INTO `sys_dict_data` VALUES (4156, 4156, 'job.ne_config_backup_remark',
|
||||
REPLACE INTO `sys_dict_data` VALUES (4157, 4157, 'job.exportOperateLog', 'Export regularly from operation log table', 'i18n_en', '', '', '1', 'supervisor', 1721902269805, '', 0, '');
|
||||
REPLACE INTO `sys_dict_data` VALUES (4158, 4158, 'job.exportIMSCDR', 'Export regularly from IMS CDR table', 'i18n_en', '', '', '1', 'supervisor', 1721902269805, '', 0, '');
|
||||
REPLACE INTO `sys_dict_data` VALUES (4159, 4159, 'job.exportSMFCDR', 'Export regularly from SMF CDR table', 'i18n_en', '', '', '1', 'supervisor', 1721902269805, '', 0, '');
|
||||
REPLACE INTO `sys_dict_data` VALUES (4160, 4160, 'table.sys_log_operate', 'Operation Log', 'i18n_en', '', '', '1', 'supervisor', 1721902269805, '', 0, '');
|
||||
REPLACE INTO `sys_dict_data` VALUES (4161, 4161, 'table.cdr_event_ims', 'Voice CDR', 'i18n_en', '', '', '1', 'supervisor', 1721902269805, '', 0, '');
|
||||
REPLACE INTO `sys_dict_data` VALUES (4162, 4162, 'table.cdr_event_smf', 'Data CDR', 'i18n_en', '', '', '1', 'supervisor', 1721902269805, '', 0, '');
|
||||
REPLACE INTO `sys_dict_data` VALUES (4163, 4163, 'table.cdr_event_smsc', 'SMS CDR', 'i18n_en', '', '', '1', 'supervisor', 1721902269805, '', 0, '');
|
||||
|
||||
|
||||
-- 多租户
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package features
|
||||
|
||||
import (
|
||||
"be.ems/features/lm"
|
||||
"be.ems/features/pm"
|
||||
"be.ems/lib/log"
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -10,8 +11,9 @@ func InitServiceEngine(r *gin.Engine) {
|
||||
log.Info("======init feature group gin.Engine")
|
||||
|
||||
// featuresGroup := r.Group("/")
|
||||
// 注册 PM 模块的路由
|
||||
// 注册 各个features 模块的路由
|
||||
pm.InitSubServiceRoute(r)
|
||||
lm.InitSubServiceRoute(r)
|
||||
|
||||
// return featuresGroup
|
||||
}
|
||||
|
||||
124
features/lm/file_export/controller.go
Normal file
124
features/lm/file_export/controller.go
Normal file
@@ -0,0 +1,124 @@
|
||||
package file_export
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"be.ems/lib/file"
|
||||
"be.ems/lib/log"
|
||||
"be.ems/lib/services"
|
||||
"be.ems/src/framework/datasource"
|
||||
"be.ems/src/framework/i18n"
|
||||
"be.ems/src/framework/utils/ctx"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type SysJobResponse struct {
|
||||
SysJob
|
||||
TableName string `json:"tableName"`
|
||||
TableDisplay string `json:"tableDisplay"`
|
||||
FilePath string `json:"filePath"`
|
||||
}
|
||||
|
||||
type TargetParams struct {
|
||||
Duration int `json:"duration"`
|
||||
TableName string `json:"tableName"`
|
||||
Columns string `json:"columns"` // exported column name of time string
|
||||
TimeCol string `json:"timeCol"` // time stamp of column name
|
||||
TimeUnit string `json:"timeUnit"` // timestamp unit: second/micro/milli
|
||||
Extras string `json:"extras"` // extras condition for where
|
||||
FilePath string `json:"filePath"` // file path
|
||||
}
|
||||
|
||||
func (m *SysJob) GetFileExportTable(c *gin.Context) {
|
||||
var results []SysJob
|
||||
|
||||
err := datasource.DefaultDB().Table(m.TableName()).Where("invoke_target=? and status=1", INVOKE_FILE_EXPORT).
|
||||
Find(&results).Error
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, services.ErrResp(err.Error()))
|
||||
return
|
||||
}
|
||||
language := ctx.AcceptLanguage(c)
|
||||
var response []SysJobResponse
|
||||
for _, job := range results {
|
||||
var params TargetParams
|
||||
if err := json.Unmarshal(job.TargetParams, ¶ms); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, services.ErrResp(err.Error()))
|
||||
return
|
||||
}
|
||||
TableDisplay := i18n.TKey(language, "table."+params.TableName)
|
||||
if TableDisplay == "" {
|
||||
TableDisplay = params.TableName
|
||||
}
|
||||
response = append(response, SysJobResponse{
|
||||
SysJob: job,
|
||||
TableName: params.TableName,
|
||||
TableDisplay: TableDisplay,
|
||||
FilePath: params.FilePath,
|
||||
})
|
||||
}
|
||||
c.JSON(http.StatusOK, services.DataResp(response))
|
||||
}
|
||||
|
||||
func (m *FileExport) GetFileList(c *gin.Context) {
|
||||
dir := c.Query("path")
|
||||
suffix := c.Query("suffix")
|
||||
|
||||
files, err := file.GetFileInfo(dir, suffix)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.JSON(http.StatusInternalServerError, services.ErrResp(err.Error()))
|
||||
}
|
||||
total := int64(len(files))
|
||||
c.JSON(http.StatusOK, services.TotalDataResp(files, total))
|
||||
}
|
||||
|
||||
func (m *FileExport) Total(c *gin.Context) {
|
||||
dir := c.Query("path")
|
||||
|
||||
fileCount, dirCount, err := file.GetFileAndDirCount(dir)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.JSON(http.StatusInternalServerError, services.ErrResp(err.Error()))
|
||||
return
|
||||
}
|
||||
total := fileCount + dirCount
|
||||
c.JSON(http.StatusOK, services.TotalResp(int64(total)))
|
||||
}
|
||||
|
||||
func (m *FileExport) DownloadHandler(c *gin.Context) {
|
||||
dir := c.Query("path")
|
||||
fileName := c.Param("fileName")
|
||||
filePath := filepath.Join(dir, fileName)
|
||||
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, services.ErrResp(err.Error()))
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
if _, err := os.Stat(filePath); os.IsNotExist(err) {
|
||||
c.JSON(http.StatusNotFound, services.ErrResp(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
c.Header("Content-Disposition", "attachment; filename="+fileName)
|
||||
c.Header("Content-Type", "application/octet-stream")
|
||||
c.File(filePath)
|
||||
}
|
||||
|
||||
func (m *FileExport) Delete(c *gin.Context) {
|
||||
fileName := c.Param("fileName")
|
||||
dir := c.Query("path")
|
||||
filePath := filepath.Join(dir, fileName)
|
||||
|
||||
if err := os.Remove(filePath); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, services.ErrResp(err.Error()))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusNoContent, nil) // 204 No Content
|
||||
}
|
||||
34
features/lm/file_export/model.go
Normal file
34
features/lm/file_export/model.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package file_export
|
||||
|
||||
import (
|
||||
"gorm.io/datatypes"
|
||||
)
|
||||
|
||||
const (
|
||||
INVOKE_FILE_EXPORT = "exportTable"
|
||||
)
|
||||
|
||||
type SysJob struct {
|
||||
JobID int64 `gorm:"column:job_id;primary_key;auto_increment" json:"job_id"` //任务ID
|
||||
InvokeTarget string `gorm:"column:invoke_target" json:"invoke_target"` //调用目标字符串
|
||||
TargetParams datatypes.JSON `gorm:"column:target_params;type:json" json:"target_params,omitempty"` //调用目标传入参数
|
||||
}
|
||||
|
||||
func (m *SysJob) TableName() string {
|
||||
return "sys_job"
|
||||
}
|
||||
|
||||
type FileExport struct {
|
||||
FileType string `json:"fileType"` // 文件类型
|
||||
FileMode string `json:"fileMode"` // 文件的权限
|
||||
LinkCount int64 `json:"linkCount"` // 硬链接数目
|
||||
Owner string `json:"owner"` // 所属用户
|
||||
Group string `json:"group"` // 所属组
|
||||
Size int64 `json:"size"` // 文件的大小
|
||||
ModifiedTime int64 `json:"modifiedTime"` // 最后修改时间,单位为秒
|
||||
FileName string `json:"fileName"` // 文件的名称
|
||||
}
|
||||
|
||||
func (m *FileExport) TableName() string {
|
||||
return "file_export"
|
||||
}
|
||||
40
features/lm/file_export/route.go
Normal file
40
features/lm/file_export/route.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package file_export
|
||||
|
||||
import (
|
||||
"be.ems/src/framework/middleware"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// Register Routes for file_export
|
||||
func Register(r *gin.RouterGroup) {
|
||||
|
||||
lmTable := r.Group("/table")
|
||||
{
|
||||
var m *SysJob
|
||||
lmTable.GET("/list",
|
||||
middleware.PreAuthorize(nil),
|
||||
m.GetFileExportTable,
|
||||
)
|
||||
|
||||
}
|
||||
lmFile := r.Group("/file")
|
||||
{
|
||||
var f *FileExport
|
||||
lmFile.GET("/list",
|
||||
middleware.PreAuthorize(nil),
|
||||
f.GetFileList,
|
||||
)
|
||||
lmFile.GET("/total",
|
||||
middleware.PreAuthorize(nil),
|
||||
f.Total,
|
||||
)
|
||||
lmFile.GET("/:fileName",
|
||||
middleware.PreAuthorize(nil),
|
||||
f.DownloadHandler,
|
||||
)
|
||||
lmFile.DELETE("/:fileName",
|
||||
middleware.PreAuthorize(nil),
|
||||
f.Delete,
|
||||
)
|
||||
}
|
||||
}
|
||||
17
features/lm/service.go
Normal file
17
features/lm/service.go
Normal file
@@ -0,0 +1,17 @@
|
||||
// log management package
|
||||
|
||||
package lm
|
||||
|
||||
import (
|
||||
"be.ems/features/lm/file_export"
|
||||
"be.ems/lib/log"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func InitSubServiceRoute(r *gin.Engine) {
|
||||
log.Info("======init Log management group gin.Engine")
|
||||
|
||||
lmGroup := r.Group("/lm")
|
||||
// register sub modules routes
|
||||
file_export.Register(lmGroup)
|
||||
}
|
||||
84
lib/file/file_linux.go
Normal file
84
lib/file/file_linux.go
Normal file
@@ -0,0 +1,84 @@
|
||||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package file
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
type FileInfo struct {
|
||||
FileType string `json:"fileType"` // 文件类型
|
||||
FileMode string `json:"fileMode"` // 文件的权限
|
||||
LinkCount int64 `json:"linkCount"` // 硬链接数目
|
||||
Owner string `json:"owner"` // 所属用户
|
||||
Group string `json:"group"` // 所属组
|
||||
Size int64 `json:"size"` // 文件的大小
|
||||
ModifiedTime int64 `json:"modifiedTime"` // 最后修改时间,单位为秒
|
||||
FileName string `json:"fileName"` // 文件的名称
|
||||
}
|
||||
|
||||
func GetFileInfo(dir, suffix string) ([]FileInfo, error) {
|
||||
var files []FileInfo
|
||||
|
||||
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if path == dir {
|
||||
return nil // 跳过当前目录
|
||||
}
|
||||
|
||||
fileType := "file"
|
||||
if info.IsDir() {
|
||||
fileType = "directory"
|
||||
} else if info.Mode()&os.ModeSymlink != 0 {
|
||||
fileType = "symlink"
|
||||
}
|
||||
|
||||
// check if match suffix
|
||||
if (suffix != "" && filepath.Ext(path) == suffix) || suffix == "" {
|
||||
fileInfo := FileInfo{
|
||||
FileType: fileType,
|
||||
FileMode: info.Mode().String(),
|
||||
LinkCount: int64(info.Sys().(*syscall.Stat_t).Nlink),
|
||||
Owner: fmt.Sprintf("%d", info.Sys().(*syscall.Stat_t).Uid),
|
||||
Group: fmt.Sprintf("%d", info.Sys().(*syscall.Stat_t).Gid),
|
||||
Size: info.Size(),
|
||||
ModifiedTime: info.ModTime().Unix(),
|
||||
FileName: info.Name(),
|
||||
}
|
||||
files = append(files, fileInfo)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return files, nil
|
||||
}
|
||||
|
||||
func GetFileAndDirCount(dir string) (int, int, error) {
|
||||
var fileCount, dirCount int
|
||||
|
||||
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if path == dir {
|
||||
return nil // 跳过当前目录
|
||||
}
|
||||
if info.IsDir() {
|
||||
dirCount++
|
||||
} else {
|
||||
fileCount++
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
return fileCount, dirCount, err
|
||||
}
|
||||
82
lib/file/file_windows.go
Normal file
82
lib/file/file_windows.go
Normal file
@@ -0,0 +1,82 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package file
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
type FileInfo struct {
|
||||
FileType string `json:"fileType"` // 文件类型
|
||||
FileMode string `json:"fileMode"` // 文件的权限
|
||||
LinkCount int64 `json:"linkCount"` // 硬链接数目
|
||||
Owner string `json:"owner"` // 所属用户
|
||||
Group string `json:"group"` // 所属组
|
||||
Size int64 `json:"size"` // 文件的大小
|
||||
ModifiedTime int64 `json:"modifiedTime"` // 最后修改时间,单位为秒
|
||||
FileName string `json:"fileName"` // 文件的名称
|
||||
}
|
||||
|
||||
func GetFileInfo(dir, suffix string) ([]FileInfo, error) {
|
||||
var files []FileInfo
|
||||
|
||||
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if path == dir {
|
||||
return nil // 跳过当前目录
|
||||
}
|
||||
|
||||
fileType := "file"
|
||||
if info.IsDir() {
|
||||
fileType = "directory"
|
||||
} else if info.Mode()&os.ModeSymlink != 0 {
|
||||
fileType = "symlink"
|
||||
}
|
||||
|
||||
// check if match suffix
|
||||
if (suffix != "" && filepath.Ext(path) == suffix) || suffix == "" {
|
||||
fileInfo := FileInfo{
|
||||
FileType: fileType,
|
||||
FileMode: info.Mode().String(),
|
||||
LinkCount: 0,
|
||||
Owner: "N/A",
|
||||
Group: "N/A",
|
||||
Size: info.Size(),
|
||||
ModifiedTime: info.ModTime().Unix(),
|
||||
FileName: info.Name(),
|
||||
}
|
||||
files = append(files, fileInfo)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return files, nil
|
||||
}
|
||||
|
||||
func GetFileAndDirCount(dir string) (int, int, error) {
|
||||
var fileCount, dirCount int
|
||||
|
||||
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if path == dir {
|
||||
return nil // 跳过当前目录
|
||||
}
|
||||
if info.IsDir() {
|
||||
dirCount++
|
||||
} else {
|
||||
fileCount++
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
return fileCount, dirCount, err
|
||||
}
|
||||
Reference in New Issue
Block a user