This commit is contained in:
XuanLee-HEALER
2025-11-10 15:30:21 +08:00
parent 4716b82f29
commit df39693dff
43 changed files with 6864 additions and 5 deletions

587
trie.go.bak Normal file
View File

@@ -0,0 +1,587 @@
package main
import (
"fmt"
"path"
"strings"
"time"
"github.com/dghubble/trie"
)
// PathNode 表示路径节点用于BFS遍历
type PathNode struct {
Path string // 完整路径
Level int // 层级深度
ParentID *int64 // 父节点ID根节点为nil
IsFile bool // 是否是文件
}
// MFiles 表示 m_files 表的记录
type MFiles struct {
ID int64 `json:"id"`
FileName string `json:"file_name"`
FileType string `json:"file_type"`
FileSize int64 `json:"file_size"`
FileKey string `json:"file_key"`
CreatedAt string `json:"created_at"`
}
// MDocuments 表示 m_documents 表的记录
type MDocuments struct {
ID int64 `json:"id"`
ParentID int64 `json:"parent_id"`
FileID *int64 `json:"file_id"`
Type string `json:"type"` // 'file' or 'dir'
Path string `json:"path"`
ProjectID int64 `json:"project_id"`
UID int64 `json:"uid"`
IsDelete int8 `json:"is_delete"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
DeletedAt *string `json:"deleted_at"`
Alias string `json:"alias"`
}
// DatabaseSimulator 数据库模拟器
type DatabaseSimulator struct {
filesTable map[string]*MFiles // file_key -> record
documentsTable map[string]*MDocuments // path -> record
nextFileID int64
nextDocID int64
projectID int64 // 固定项目ID
uid int64 // 固定用户ID
}
// NewDatabaseSimulator 创建数据库模拟器
func NewDatabaseSimulator(projectID, uid int64) *DatabaseSimulator {
return &DatabaseSimulator{
filesTable: make(map[string]*MFiles),
documentsTable: make(map[string]*MDocuments),
nextFileID: 1,
nextDocID: 1,
projectID: projectID,
uid: uid,
}
}
// isFilePath 判断路径是否为文件(基于是否有扩展名)
func isFilePath(path string) bool {
// 获取路径的最后一部分
parts := strings.Split(path, "/")
if len(parts) == 0 {
return false
}
fileName := parts[len(parts)-1]
// 如果包含点且不是以点开头,则认为是文件
return strings.Contains(fileName, ".") && !strings.HasPrefix(fileName, ".")
}
// getFileName 从路径获取文件名
func getFileName(path string) string {
parts := strings.Split(path, "/")
if len(parts) == 0 {
return ""
}
return parts[len(parts)-1]
}
// getFileExtension 获取文件扩展名
func getFileExtension(fileName string) string {
parts := strings.Split(fileName, ".")
if len(parts) <= 1 {
return ""
}
return parts[len(parts)-1]
}
// ProcessFileOperation 处理文件操作(插入或更新 m_files 表)
func (db *DatabaseSimulator) ProcessFileOperation(fileKey string) (*MFiles, string) {
fileName := getFileName(fileKey)
fileExt := getFileExtension(fileName)
currentTime := time.Now().Format("2006-01-02 15:04:05.000")
if existingFile, exists := db.filesTable[fileKey]; exists {
// 文件已存在,更新记录
updateSQL := fmt.Sprintf(`UPDATE m_files SET
file_name = '%s',
file_type = '%s',
updated_at = '%s'
WHERE file_key = '%s';`,
fileName, fileExt, currentTime, fileKey)
existingFile.FileName = fileName
existingFile.FileType = fileExt
return existingFile, updateSQL
} else {
// 插入新文件记录
newFile := &MFiles{
ID: db.nextFileID,
FileName: fileName,
FileType: fileExt,
FileSize: 1024, // 模拟文件大小
FileKey: fileKey,
CreatedAt: currentTime,
}
insertSQL := fmt.Sprintf(`INSERT INTO m_files (id, file_name, file_type, file_size, file_key, created_at)
VALUES (%d, '%s', '%s', %d, '%s', '%s');`,
newFile.ID, newFile.FileName, newFile.FileType, newFile.FileSize, newFile.FileKey, newFile.CreatedAt)
db.filesTable[fileKey] = newFile
db.nextFileID++
return newFile, insertSQL
}
}
// ProcessDocumentOperation 处理文档操作(插入或跳过 m_documents 表)
func (db *DatabaseSimulator) ProcessDocumentOperation(path string, parentID *int64, isFile bool, fileID *int64) (*MDocuments, string) {
currentTime := time.Now().Format("2006-01-02 15:04:05.000")
// 检查是否已存在且未删除
if existingDoc, exists := db.documentsTable[path]; exists && existingDoc.IsDelete == 0 {
// 文档已存在且未删除,不处理
return existingDoc, ""
}
// 确定父级ID如果没有父级则为0
actualParentID := int64(0)
if parentID != nil {
actualParentID = *parentID
}
// 确定文档类型
docType := "dir"
if isFile {
docType = "file"
}
// 插入新文档记录
newDoc := &MDocuments{
ID: db.nextDocID,
ParentID: actualParentID,
FileID: fileID,
Type: docType,
Path: path,
ProjectID: db.projectID,
UID: db.uid,
IsDelete: 0,
CreatedAt: currentTime,
UpdatedAt: currentTime,
DeletedAt: nil,
Alias: getFileName(path),
}
fileIDStr := "NULL"
if fileID != nil {
fileIDStr = fmt.Sprintf("%d", *fileID)
}
insertSQL := fmt.Sprintf(`INSERT INTO m_documents (id, parent_id, file_id, type, path, project_id, uid, is_delete, created_at, updated_at, alias)
VALUES (%d, %d, %s, '%s', '%s', %d, %d, %d, '%s', '%s', '%s');`,
newDoc.ID, newDoc.ParentID, fileIDStr, newDoc.Type, newDoc.Path,
newDoc.ProjectID, newDoc.UID, newDoc.IsDelete, newDoc.CreatedAt, newDoc.UpdatedAt, newDoc.Alias)
db.documentsTable[path] = newDoc
db.nextDocID++
return newDoc, insertSQL
}
// PathTrieBuilder 路径字典树构建器
type PathTrieBuilder struct {
trie *trie.PathTrie
}
// NewPathTrieBuilder 创建新的路径字典树构建器
func NewPathTrieBuilder() *PathTrieBuilder {
return &PathTrieBuilder{
trie: trie.NewPathTrie(),
}
}
// BuildTrie 构建字典树
func (ptb *PathTrieBuilder) BuildTrie(paths []string) {
for _, path := range paths {
// 将路径按分隔符分割
parts := strings.Split(path, "/")
// 逐步构建路径并插入到trie中
currentPath := ""
for i, part := range parts {
if i == 0 {
currentPath = part
} else {
currentPath = currentPath + "/" + part
}
// 将当前路径插入到trie中值为路径的层级
ptb.trie.Put(currentPath, len(strings.Split(currentPath, "/")))
}
}
}
// BFSTraverse 使用BFS遍历字典树构建带有父子关系的路径节点
func (ptb *PathTrieBuilder) BFSTraverse() []PathNode {
var result []PathNode
var queue []PathNode
// 获取所有根路径(第一层)
ptb.trie.Walk(func(key string, value interface{}) error {
level := value.(int)
if level == 1 { // 第一层的路径
queue = append(queue, PathNode{
Path: key,
Level: level,
ParentID: nil,
IsFile: isFilePath(key),
})
}
return nil
})
// BFS遍历
for len(queue) > 0 {
// 取出队列头部节点
current := queue[0]
queue = queue[1:]
// 添加到结果中
result = append(result, current)
// 查找当前节点的子节点
ptb.trie.Walk(func(key string, value interface{}) error {
level := value.(int)
// 如果是当前路径的直接子路径
if strings.HasPrefix(key, current.Path+"/") && level == current.Level+1 {
// 确保是直接子路径,而不是孙子路径
remainingPath := strings.TrimPrefix(key, current.Path+"/")
if !strings.Contains(remainingPath, "/") {
childNode := PathNode{
Path: key,
Level: level,
ParentID: nil,
IsFile: isFilePath(key),
}
queue = append(queue, childNode)
}
}
return nil
})
}
return result
}
// ProcessPathsWithDatabase 处理路径并生成数据库操作
func ProcessPathsWithDatabase(paths []PathNode) {
fmt.Println("=== 模拟数据库操作 ===")
fmt.Println("按BFS顺序处理路径并生成SQL语句")
fmt.Println()
// 创建数据库模拟器
db := NewDatabaseSimulator(1, 1001) // projectID=1, uid=1001
pathToDocIDMap := make(map[string]int64) // 路径到document ID的映射
// 预先插入一些已存在的记录
fmt.Println("=== 预置数据库记录 ===")
existingDocs := []*MDocuments{
{ID: 100, ParentID: 0, FileID: nil, Type: "dir", Path: "home", ProjectID: 1, UID: 1001, IsDelete: 0, Alias: "home"},
{ID: 101, ParentID: 100, FileID: nil, Type: "dir", Path: "home/user", ProjectID: 1, UID: 1001, IsDelete: 0, Alias: "user"},
}
for _, doc := range existingDocs {
db.documentsTable[doc.Path] = doc
pathToDocIDMap[doc.Path] = doc.ID
if doc.ID >= db.nextDocID {
db.nextDocID = doc.ID + 1
}
fmt.Printf("已存在文档: ID=%d, Path=%s, Type=%s, ParentID=%d\n",
doc.ID, doc.Path, doc.Type, doc.ParentID)
}
fmt.Println()
// 统计计数器
var (
filesInserted = 0
filesUpdated = 0
dirsInserted = 0
dirsSkipped = 0
sqlStatements []string
)
// 处理BFS遍历的路径
for i, node := range paths {
// 获取父节点ID
parentID := getParentDocumentID(node.Path, pathToDocIDMap)
fmt.Printf("[%03d] 处理路径: %s (Level %d, Type: %s)\n",
i+1, node.Path, node.Level, func() string {
if node.IsFile {
return "文件"
}
return "目录"
}())
if node.IsFile {
// 处理文件
fmt.Printf(" → 1. 处理文件表 (m_files):\n")
// 先处理 m_files 表
fileRecord, fileSQL := db.ProcessFileOperation(node.Path)
if strings.Contains(fileSQL, "INSERT") {
fmt.Printf(" INSERT文件: %s\n", fileSQL)
filesInserted++
} else if strings.Contains(fileSQL, "UPDATE") {
fmt.Printf(" UPDATE文件: %s\n", fileSQL)
filesUpdated++
}
sqlStatements = append(sqlStatements, fileSQL)
// 再处理 m_documents 表
fmt.Printf(" → 2. 处理文档表 (m_documents):\n")
docRecord, docSQL := db.ProcessDocumentOperation(node.Path, parentID, true, &fileRecord.ID)
if docSQL != "" {
fmt.Printf(" INSERT文档: %s\n", docSQL)
pathToDocIDMap[node.Path] = docRecord.ID
dirsInserted++
sqlStatements = append(sqlStatements, docSQL)
} else {
fmt.Printf(" SKIP文档: 路径已存在且未删除\n")
dirsSkipped++
}
} else {
// 处理目录
fmt.Printf(" → 处理文档表 (m_documents) - 目录:\n")
docRecord, docSQL := db.ProcessDocumentOperation(node.Path, parentID, false, nil)
if docSQL != "" {
fmt.Printf(" INSERT目录: %s\n", docSQL)
pathToDocIDMap[node.Path] = docRecord.ID
dirsInserted++
sqlStatements = append(sqlStatements, docSQL)
} else {
fmt.Printf(" SKIP目录: 路径已存在且未删除\n")
// 即使跳过也要记录ID供子节点使用
if existingDoc, exists := db.documentsTable[node.Path]; exists {
pathToDocIDMap[node.Path] = existingDoc.ID
}
dirsSkipped++
}
}
fmt.Println()
}
// 输出统计信息
fmt.Println("=== 操作统计 ===")
fmt.Printf("总共处理路径: %d 个\n", len(paths))
fmt.Printf("文件插入: %d 条\n", filesInserted)
fmt.Printf("文件更新: %d 条\n", filesUpdated)
fmt.Printf("目录插入: %d 条\n", dirsInserted)
fmt.Printf("目录跳过: %d 条\n", dirsSkipped)
fmt.Printf("生成SQL语句: %d 条\n", len(sqlStatements))
fmt.Println("\n=== 完整SQL执行顺序 ===")
for i, sql := range sqlStatements {
fmt.Printf("[%03d] %s\n", i+1, sql)
}
}
// getParentDocumentID 根据路径获取父文档ID
func getParentDocumentID(path string, pathToDocIDMap map[string]int64) *int64 {
parts := strings.Split(path, "/")
if len(parts) <= 1 {
return nil // 根节点没有父节点
}
// 构建父路径
parentParts := parts[:len(parts)-1]
parentPath := strings.Join(parentParts, "/")
if parentID, exists := pathToDocIDMap[parentPath]; exists {
return &parentID
}
return nil
} // GenerateSamplePaths 生成示例路径数据,包含文件和目录
func GenerateSamplePaths() []string {
return []string{
// 文档目录相关
"home/user/documents",
"home/user/documents/readme.txt",
"home/user/documents/project.pdf",
"home/user/downloads",
"home/user/downloads/setup.exe",
"home/user/pictures/vacation",
"home/user/pictures/vacation/beach.jpg",
"home/user/pictures/vacation/sunset.png",
"home/user/pictures/family",
"home/user/pictures/family/photo1.jpg",
// 管理员目录
"home/admin/config",
"home/admin/config/app.yaml",
"home/admin/logs/error",
"home/admin/logs/error/app.log",
"home/admin/logs/access",
"home/admin/logs/access/access.log",
// 系统日志
"var/log/system",
"var/log/system/boot.log",
"var/log/application/debug",
"var/log/application/debug/debug.log",
"var/log/application/info",
"var/log/application/info/info.log",
// 缓存目录
"var/cache/temp",
"var/cache/temp/temp_file.tmp",
"var/cache/sessions",
"var/cache/sessions/session_123.dat",
// 用户程序目录
"usr/local/bin",
"usr/local/bin/app",
"usr/local/lib/python",
"usr/local/lib/python/module.py",
"usr/local/lib/nodejs",
"usr/local/lib/nodejs/package.json",
// 共享资源
"usr/share/docs",
"usr/share/docs/manual.pdf",
"usr/share/icons/themes",
"usr/share/icons/themes/icon.png",
// 配置文件
"etc/nginx/sites",
"etc/nginx/sites/default.conf",
"etc/nginx/conf.d",
"etc/nginx/conf.d/ssl.conf",
"etc/ssl/certs",
"etc/ssl/certs/server.crt",
"etc/ssh/keys",
"etc/ssh/keys/id_rsa.pub",
// 软件目录
"opt/software/database",
"opt/software/database/config.ini",
"opt/software/webserver",
"opt/software/webserver/httpd.conf",
// 临时上传
"tmp/uploads/images",
"tmp/uploads/images/upload.jpg",
"tmp/uploads/documents",
"tmp/uploads/documents/doc.docx",
"tmp/cache/thumbnails",
"tmp/cache/thumbnails/thumb.jpg",
// 数据备份
"data/backups/daily",
"data/backups/daily/backup_20240829.sql",
"data/backups/weekly",
"data/backups/weekly/backup_week34.tar.gz",
"data/exports/csv",
"data/exports/csv/users.csv",
"data/exports/json",
"data/exports/json/data.json",
}
}
func main() {
// // 密钥长度32字节256位
// key := make([]byte, 32)
// // Nonce长度12字节96位
// nonce := make([]byte, 12)
// // 使用crypto/rand包安全地生成随机字节
// _, err := rand.Read(key)
// if err != nil {
// log.Fatalf("Error generating key: %v", err)
// }
// _, err = rand.Read(nonce)
// if err != nil {
// log.Fatalf("Error generating nonce: %v", err)
// }
// // 打印生成的字节数组的字面值
// fmt.Printf("生成的ChaCha20密钥\n%#v\n", key)
// fmt.Printf("生成的ChaCha20 Nonce\n%#v\n", nonce)
// db, err := gorm.Open(mysql.Open(""), &gorm.Config{
// Logger: logger.Default.LogMode(logger.Info),
// })
// if err != nil {
// return nil, err
// }
// // 配置连接池
// sqlDB, err := db.DB()
// if err != nil {
// return nil, err
// }
// // 设置连接池参数
// if conf.MaxIdleConns > 0 {
// sqlDB.SetMaxIdleConns(conf.MaxIdleConns)
// }
// if conf.MaxOpenConns > 0 {
// sqlDB.SetMaxOpenConns(conf.MaxOpenConns)
// }
// if conf.ConnMaxLifetime > 0 {
// sqlDB.SetConnMaxLifetime(time.Duration(conf.ConnMaxLifetime) * time.Second)
// }
fmt.Println("=== 路径字典树构建和数据库操作演示 ===")
fmt.Println()
// 生成示例路径
paths := GenerateSamplePaths()
fmt.Printf("生成了 %d 个示例路径(包含文件和目录):\n", len(paths))
for i, path := range paths {
pathType := "目录"
if isFilePath(path) {
pathType = "文件"
}
fmt.Printf(" [%02d] %s (%s)\n", i+1, path, pathType)
}
fmt.Println()
// 创建路径字典树构建器
builder := NewPathTrieBuilder()
// 构建字典树
fmt.Println("正在构建字典树...")
builder.BuildTrie(paths)
fmt.Println("字典树构建完成!")
fmt.Println()
builder.trie.Walk(func(key string, value interface{}) error {
println("key is ", key)
return nil
})
// BFS遍历字典树
// fmt.Println("开始BFS遍历字典树...")
// bfsResult := builder.BFSTraverse()
// fmt.Printf("BFS遍历完成共获得 %d 个节点\n", len(bfsResult))
// fmt.Println()
// 处理路径并生成数据库操作
// ProcessPathsWithDatabase(bfsResult)
fmt.Println()
fmt.Println("=== 程序执行完成 ===")
p := "a/b/c"
dir, base := path.Split(p)
println("dir:", dir, "base:", base)
p = "a/b/c/"
dir, base = path.Split(p)
println("dir:", dir, "base:", base)
println("tst", strings.Join([]string{"good"}, "/"))
}