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"}, "/")) }