add: custom kpi and export log&cdr file
This commit is contained in:
111
lib/eval/evaluate.go
Normal file
111
lib/eval/evaluate.go
Normal file
@@ -0,0 +1,111 @@
|
||||
package evaluate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Parse and caculate expression
|
||||
func CalcExpr(expr string, paramValues map[string]any) (float64, error) {
|
||||
// match parameter with ''
|
||||
re := regexp.MustCompile(`'([^']+)'`)
|
||||
matches := re.FindAllStringSubmatch(expr, -1)
|
||||
|
||||
// replace to value
|
||||
for _, match := range matches {
|
||||
paramName := match[1]
|
||||
value, exists := paramValues[paramName]
|
||||
if !exists {
|
||||
return 0, fmt.Errorf("parameter '%s' not found", paramName)
|
||||
}
|
||||
|
||||
expr = strings.Replace(expr, match[0], fmt.Sprintf("%v", value), 1)
|
||||
}
|
||||
|
||||
// expression to evaluate
|
||||
result, err := evalExpr(expr)
|
||||
return result, err
|
||||
}
|
||||
|
||||
// eval 解析和计算表达式
|
||||
func evalExpr(expr string) (float64, error) {
|
||||
//fset := token.NewFileSet()
|
||||
node, err := parser.ParseExpr(expr)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return evalNode(node)
|
||||
}
|
||||
|
||||
// EvaluateExpr 解析并计算给定的表达式
|
||||
func EvalExpr(expr string, values map[string]any) (float64, error) {
|
||||
// 解析表达式
|
||||
node, err := parser.ParseExpr(expr)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// 遍历 AST 并替换变量
|
||||
ast.Inspect(node, func(n ast.Node) bool {
|
||||
if ident, ok := n.(*ast.Ident); ok {
|
||||
if val, ok := values[ident.Name]; ok {
|
||||
// 替换标识符为对应值
|
||||
ident.Name = fmt.Sprintf("%v", val)
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
// 计算表达式
|
||||
return evalNode(node)
|
||||
}
|
||||
|
||||
// eval 递归计算 AST 节点
|
||||
func evalNode(node ast.Node) (float64, error) {
|
||||
var result float64
|
||||
|
||||
switch n := node.(type) {
|
||||
case *ast.BinaryExpr:
|
||||
left, err := evalNode(n.X)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
right, err := evalNode(n.Y)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
switch n.Op {
|
||||
case token.ADD:
|
||||
result = left + right
|
||||
case token.SUB:
|
||||
result = left - right
|
||||
case token.MUL:
|
||||
result = left * right
|
||||
case token.QUO:
|
||||
result = left / right
|
||||
}
|
||||
case *ast.BasicLit:
|
||||
var err error
|
||||
result, err = strconv.ParseFloat(n.Value, 64)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
case *ast.Ident:
|
||||
val, err := strconv.ParseFloat(n.Name, 64)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("unsupported expression: %s", n.Name)
|
||||
}
|
||||
result = val
|
||||
case *ast.ParenExpr:
|
||||
return evalNode(n.X) // 递归评估括号中的表达式
|
||||
default:
|
||||
return 0, fmt.Errorf("unsupported expression: %T", n)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
27
lib/file/file.go
Normal file
27
lib/file/file.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package file
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
63
lib/file/file_linux.go
Normal file
63
lib/file/file_linux.go
Normal file
@@ -0,0 +1,63 @@
|
||||
//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
|
||||
}
|
||||
61
lib/file/file_windows.go
Normal file
61
lib/file/file_windows.go
Normal file
@@ -0,0 +1,61 @@
|
||||
//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
|
||||
}
|
||||
35
lib/services/response.go
Normal file
35
lib/services/response.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package services
|
||||
|
||||
const (
|
||||
CODE_FAIL = 0
|
||||
CODE_SUCC = 1
|
||||
)
|
||||
|
||||
func ErrResp(msg string) map[string]any {
|
||||
return map[string]any{"code": CODE_FAIL, "message": msg}
|
||||
}
|
||||
|
||||
func DataResp(data any) map[string]any {
|
||||
return map[string]any{"code": CODE_SUCC, "data": data}
|
||||
}
|
||||
|
||||
func SuccMessageResp() map[string]any {
|
||||
return map[string]any{"code": CODE_SUCC, "message": "success"}
|
||||
}
|
||||
|
||||
func TotalResp(total int64) map[string]any {
|
||||
return map[string]any{"code": CODE_SUCC, "total": total}
|
||||
}
|
||||
|
||||
func TotalDataResp(data any, total any) map[string]any {
|
||||
return map[string]any{"code": CODE_SUCC, "data": data, "total": total}
|
||||
}
|
||||
|
||||
func SuccResp(va map[string]any) map[string]any {
|
||||
resp := make(map[string]any)
|
||||
resp["code"] = CODE_SUCC
|
||||
for k, v := range va {
|
||||
resp[k] = v
|
||||
}
|
||||
return resp
|
||||
}
|
||||
Reference in New Issue
Block a user