package filesystem import ( "context" "encoding/base64" "errors" "fmt" "io" "io/fs" "log/slog" "os" "os/exec" "path/filepath" "runtime" "strings" "sync" "time" "u-desk/internal/common" ) const maxReadWriteSize = 10 * 1024 * 1024 // 10MB 读写上限 // FileOperationResult 文件操作结果 type FileOperationResult struct { Path string `json:"path"` Name string `json:"name"` Size int64 `json:"size"` SizeStr string `json:"size_str,omitempty"` IsDir bool `json:"is_dir"` ModTime string `json:"mod_time,omitempty"` Mode string `json:"mode,omitempty"` OldPath string `json:"old_path,omitempty"` // 仅重命名操作时有值 Deleted bool `json:"deleted,omitempty"` // 仅删除操作时有值 } // FileSystemService 文件系统服务 // 统一管理所有文件系统相关的功能,使用依赖注入而非全局变量 type FileSystemService struct { // 核心组件 config *Config pathValidator PathValidator fileTypeManager FileTypeManager // 基础设施组件 auditLogger *AuditLogger recycleBin *RecycleBin lockChecker *FileLockChecker // 状态管理 mu sync.RWMutex initialized bool } // NewFileSystemService 创建新的文件系统服务 // 使用依赖注入,所有组件通过参数传入,便于测试和替换 func NewFileSystemService(config *Config) (*FileSystemService, error) { if config == nil { config = DefaultConfig() } service := &FileSystemService{ config: config, pathValidator: NewPathValidator(config), fileTypeManager: NewFileTypeManager(config), } // 初始化基础设施组件 if err := service.initializeComponents(); err != nil { return nil, fmt.Errorf("初始化文件系统服务失败: %w", err) } service.initialized = true return service, nil } // initializeComponents 初始化各个组件 func (s *FileSystemService) initializeComponents() error { // 1. 初始化审计日志 if s.config.Features.AuditLog { if err := s.initAuditLogger(); err != nil { return fmt.Errorf("初始化审计日志失败: %w", err) } } // 2. 初始化回收站 if s.config.Features.RecycleBin { if err := s.initRecycleBin(); err != nil { return fmt.Errorf("初始化回收站失败: %w", err) } } // 3. 初始化文件锁检查器 if s.config.Features.FileLockCheck { s.lockChecker = NewFileLockChecker() } return nil } // initAuditLogger 初始化审计日志 func (s *FileSystemService) initAuditLogger() error { logDir := filepath.Join(common.GetUserDataDir(), "logs") logger, err := NewAuditLogger(logDir) if err != nil { return err } s.auditLogger = logger return nil } // initRecycleBin 初始化回收站 func (s *FileSystemService) initRecycleBin() error { recycleBinPath := filepath.Join(common.GetUserDataDir(), "recycle_bin") bin, err := NewRecycleBin(recycleBinPath) if err != nil { return err } s.recycleBin = bin return nil } // ========== 核心文件操作 ========== // ReadFile 读取文件内容(限制最大 10MB) func (s *FileSystemService) ReadFile(path string) (string, error) { // 路径验证 if err := s.validatePath(path); err != nil { return "", err } // 检查文件大小,避免读取超大文件导致内存问题 info, err := os.Stat(path) if err != nil { return "", fmt.Errorf("获取文件信息失败: %v", err) } if info.Size() > maxReadWriteSize { return "", fmt.Errorf("文件过大 (%.1f MB),超过读取上限 (%d MB)", float64(info.Size())/1024/1024, maxReadWriteSize/1024/1024) } // 读取文件 data, err := os.ReadFile(path) if err != nil { return "", fmt.Errorf("读取文件失败: %v", err) } s.logRead(path, int64(len(data)), nil) return common.BytesToString(data), nil } // writeFileWithLog 内部写入实现(路径验证+大小检查+写入+日志) func (s *FileSystemService) writeFileWithLog(path string, data []byte) error { if err := s.validatePath(path); err != nil { return err } dir := filepath.Dir(path) if err := os.MkdirAll(dir, DefaultDirPermissions); err != nil { return fmt.Errorf("创建目录失败: %v", err) } if len(data) > maxReadWriteSize { return fmt.Errorf("文件过大 (%.1f MB),超过写入上限 (%d MB)", float64(len(data))/1024/1024, maxReadWriteSize/1024/1024) } if err := os.WriteFile(path, data, DefaultFilePermissions); err != nil { s.logWrite(path, int64(len(data)), err) return fmt.Errorf("写入文件失败: %v", err) } s.logWrite(path, int64(len(data)), nil) return nil } // WriteFile 写入文件 func (s *FileSystemService) WriteFile(path, content string) error { return s.writeFileWithLog(path, []byte(content)) } // SaveBase64File 将 base64 编码内容解码后写入二进制文件 func (s *FileSystemService) SaveBase64File(path, base64Content string) error { if strings.TrimSpace(base64Content) == "" { return errors.New("base64 内容不能为空") } data, err := base64.StdEncoding.DecodeString(base64Content) if err != nil { return fmt.Errorf("base64 解码失败: %v", err) } return s.writeFileWithLog(path, data) } // DeletePath 删除文件或目录 func (s *FileSystemService) DeletePath(path string) (*FileOperationResult, error) { return s.DeletePathWithContext(context.Background(), path) } // DeletePathWithContext 带上下文的删除操作,返回被删除文件的信息 func (s *FileSystemService) DeletePathWithContext(ctx context.Context, path string) (*FileOperationResult, error) { // 路径验证 if err := s.validatePath(path); err != nil { return nil, err } // 获取文件信息(在删除前保存) info, err := os.Stat(path) if err != nil { if os.IsNotExist(err) { return nil, fmt.Errorf("文件或目录不存在") } return nil, fmt.Errorf("获取文件信息失败: %v", err) } // 检查删除限制 exceeds, details, checkErr := CheckDeleteRestrictions(path, info, s.config) if checkErr != nil { return nil, checkErr } if exceeds { if s.config.Security.DeleteRestrictions.RequireConfirm { return nil, &DeleteRestrictionWarning{ Path: path, Details: details, Info: info, } } return nil, fmt.Errorf("删除限制: %s", details) } // 文件锁检查(可选) if s.lockChecker != nil { if err := s.lockChecker.SafeDeleteWithLockCheck(path); err != nil { return nil, err } } // 回收站优先:先尝试移动,失败再永久删除 if s.recycleBin != nil && !isInRecycleBin(path) { if err := s.recycleBin.MoveToRecycleBin(path); err != nil { slog.Warn("回收站移动失败,改为永久删除", "error", err) if err := s.permanentDelete(path, info); err != nil { return nil, err } } else { s.logDelete(path, info.IsDir(), info.Size(), nil) } } else { // 无回收站或已在回收站中,永久删除 if err := s.permanentDelete(path, info); err != nil { return nil, err } } // 返回被删除的文件信息,用于前端更新 return &FileOperationResult{ Path: filepath.ToSlash(path), // 统一使用正斜杠 Name: info.Name(), Size: info.Size(), SizeStr: common.FormatBytesInt(info.Size()), IsDir: info.IsDir(), ModTime: info.ModTime().Format("2006-01-02 15:04:05"), Mode: info.Mode().String(), Deleted: true, }, nil } // ListDir 列出目录内容 func (s *FileSystemService) ListDir(path string) ([]map[string]interface{}, error) { // 根路径:返回 Windows 盘符列表 if path == "/" || path == "\\" { return s.listDrives() } // 路径验证 if err := s.validatePath(path); err != nil { return nil, err } // 读取目录 entries, err := os.ReadDir(path) if err != nil { return nil, fmt.Errorf("读取目录失败: %v", err) } // 转换为结果格式 result := make([]map[string]interface{}, 0, len(entries)) for _, entry := range entries { info, err := entry.Info() if err != nil { continue } fullPath := filepath.Join(path, entry.Name()) result = append(result, map[string]interface{}{ "name": entry.Name(), "path": filepath.ToSlash(fullPath), // 统一使用正斜杠 "is_dir": entry.IsDir(), "size": info.Size(), "mod_time": info.ModTime().Format("2006-01-02 15:04:05"), }) } s.logAudit(AuditLogEntry{ Timestamp: getCurrentTimestamp(), Operation: OperationList, Path: path, IsDirectory: true, Success: true, }) return result, nil } // listDrives 列出 Windows 可用盘符 func (s *FileSystemService) listDrives() ([]map[string]interface{}, error) { result := make([]map[string]interface{}, 0) for _, drive := range "ABCDEFGHIJKLMNOPQRSTUVWXYZ" { path := string(drive) + ":/" _, err := os.Stat(path) if err != nil { continue } result = append(result, map[string]interface{}{ "name": string(drive) + ":", "path": path, "is_dir": true, "size": int64(0), }) } return result, nil } // CreateDir 创建目录,返回创建的目录信息 func (s *FileSystemService) CreateDir(path string) (*FileOperationResult, error) { if err := s.validatePath(path); err != nil { return nil, err } if err := os.MkdirAll(path, DefaultDirPermissions); err != nil { return nil, fmt.Errorf("创建目录失败: %v", err) } s.logAudit(AuditLogEntry{ Timestamp: getCurrentTimestamp(), Operation: OperationCreate, Path: path, IsDirectory: true, Success: true, }) // 获取创建的目录信息 info, err := os.Stat(path) if err != nil { // 创建成功但获取信息失败,返回基本信息 return &FileOperationResult{ Path: filepath.ToSlash(path), // 统一使用正斜杠 Name: filepath.Base(path), IsDir: true, }, nil } return &FileOperationResult{ Path: filepath.ToSlash(path), // 统一使用正斜杠 Name: info.Name(), Size: info.Size(), SizeStr: common.FormatBytesInt(info.Size()), IsDir: true, ModTime: info.ModTime().Format("2006-01-02 15:04:05"), Mode: info.Mode().String(), }, nil } // CreateFile 创建空文件,返回创建的文件信息 func (s *FileSystemService) CreateFile(path string) (*FileOperationResult, error) { if err := s.validatePath(path); err != nil { return nil, err } // 检查文件是否已存在 if _, err := os.Stat(path); err == nil { return nil, fmt.Errorf("文件已存在") } file, err := os.Create(path) if err != nil { return nil, fmt.Errorf("创建文件失败: %v", err) } file.Close() s.logAudit(AuditLogEntry{ Timestamp: getCurrentTimestamp(), Operation: OperationCreate, Path: path, IsDirectory: false, Success: true, }) // 获取创建的文件信息 info, err := os.Stat(path) if err != nil { // 创建成功但获取信息失败,返回基本信息 return &FileOperationResult{ Path: filepath.ToSlash(path), // 统一使用正斜杠 Name: filepath.Base(path), IsDir: false, Size: 0, }, nil } return &FileOperationResult{ Path: filepath.ToSlash(path), // 统一使用正斜杠 Name: info.Name(), Size: info.Size(), SizeStr: common.FormatBytesInt(info.Size()), IsDir: false, ModTime: info.ModTime().Format("2006-01-02 15:04:05"), Mode: info.Mode().String(), }, nil } // GetFileInfo 获取文件信息 func (s *FileSystemService) GetFileInfo(path string) (map[string]interface{}, error) { if err := s.validatePath(path); err != nil { return nil, err } info, err := os.Stat(path) if err != nil { if os.IsNotExist(err) { return nil, fmt.Errorf("文件或目录不存在") } return nil, fmt.Errorf("获取文件信息失败: %v", err) } return map[string]interface{}{ "name": info.Name(), "path": filepath.ToSlash(path), // 统一使用正斜杠 "size": info.Size(), "size_str": common.FormatBytesInt(info.Size()), "is_dir": info.IsDir(), "mod_time": info.ModTime().Format("2006-01-02 15:04:05"), "mode": info.Mode().String(), }, nil } // OpenPath 打开文件或目录(使用系统默认程序) func (s *FileSystemService) OpenPath(path string) error { if err := s.validatePath(path); err != nil { return err } return OpenPath(path) } // RenamePath 重命名文件或目录,返回新文件信息 func (s *FileSystemService) RenamePath(oldPath, newPath string) (*FileOperationResult, error) { // 验证旧路径 if err := s.validatePath(oldPath); err != nil { return nil, err } // 验证新路径 if err := s.validatePath(newPath); err != nil { return nil, err } // 执行重命名 if err := os.Rename(oldPath, newPath); err != nil { return nil, fmt.Errorf("重命名失败: %v", err) } s.logAudit(AuditLogEntry{ Timestamp: getCurrentTimestamp(), Operation: OperationRename, Path: newPath, OldPath: oldPath, Success: true, }) // 获取新文件信息 info, err := os.Stat(newPath) if err != nil { // 重命名成功但获取信息失败,返回基本信息 return &FileOperationResult{ Path: filepath.ToSlash(newPath), // 统一使用正斜杠 Name: filepath.Base(newPath), OldPath: filepath.ToSlash(oldPath), }, nil } return &FileOperationResult{ Path: filepath.ToSlash(newPath), // 统一使用正斜杠 Name: info.Name(), Size: info.Size(), SizeStr: common.FormatBytesInt(info.Size()), IsDir: info.IsDir(), ModTime: info.ModTime().Format("2006-01-02 15:04:05"), Mode: info.Mode().String(), OldPath: filepath.ToSlash(oldPath), }, nil } // ========== 复制与移动 ========== // checkCopyMovePaths 复制/移动共用的前置校验:路径合法、源存在、目标不存在 func (s *FileSystemService) checkCopyMovePaths(src, dst string) (os.FileInfo, error) { if err := s.validatePath(src); err != nil { return nil, err } if err := s.validatePath(dst); err != nil { return nil, err } info, err := os.Lstat(src) if err != nil { return nil, fmt.Errorf("源路径不存在: %v", err) } if _, err := os.Lstat(dst); err == nil { return nil, fmt.Errorf("目标已存在: %s", dst) } return info, nil } // logCopyMoveAudit 记录复制/移动成功审计日志 func (s *FileSystemService) logCopyMoveAudit(op AuditOperation, src, dst string, isDir bool) { s.logAudit(AuditLogEntry{ Timestamp: getCurrentTimestamp(), Operation: op, Path: dst, OldPath: src, IsDirectory: isDir, Success: true, }) } // CopyPath 递归复制文件或目录到目标路径(dst 必须不存在,唯一性由调用方保证) // onProgress 每读取一块回调一次(可为 nil),copied/total 单位字节 func (s *FileSystemService) CopyPath(src, dst string, onProgress func(copied, total int64)) error { info, err := s.checkCopyMovePaths(src, dst) if err != nil { return err } total, err := pathSize(src) if err != nil { return fmt.Errorf("统计源大小失败: %v", err) } var copied int64 if err := copyPathRecursive(src, dst, info, &copied, total, onProgress); err != nil { return fmt.Errorf("复制失败: %w", err) } s.logCopyMoveAudit(OperationCopy, src, dst, info.IsDir()) return nil } // MovePath 移动文件或目录:同盘直接重命名,跨盘回退为复制后永久删除源(不走回收站) func (s *FileSystemService) MovePath(src, dst string) error { info, err := s.checkCopyMovePaths(src, dst) if err != nil { return err } // 快路径:同盘 rename(不发进度) if err := os.Rename(src, dst); err == nil { s.logCopyMoveAudit(OperationMove, src, dst, info.IsDir()) return nil } // 跨盘:复制 + 删除源 if err := s.CopyPath(src, dst, nil); err != nil { return err } if err := os.RemoveAll(src); err != nil { return fmt.Errorf("移动后删除源失败: %v", err) } s.logCopyMoveAudit(OperationMove, src, dst, info.IsDir()) return nil } // UniquePath 返回不冲突的目标路径:重名时追加「 - 副本」「 - 副本 (2)」后缀(扩展名前插入) // 命名循环复用 common.UniqueName(与 SFTP 远端重名逻辑同源) func UniquePath(path string) string { dir, name := filepath.Split(path) unique, err := common.UniqueName(name, func(candidate string) (bool, error) { _, err := os.Lstat(filepath.Join(dir, candidate)) return err == nil, nil }) if err != nil { return path // exists 恒不返回错误,防御 } return filepath.Join(dir, unique) } // copyPathRecursive 递归复制单个路径(文件/目录/符号链接) func copyPathRecursive(src, dst string, info os.FileInfo, copied *int64, total int64, onProgress func(copied, total int64)) error { switch { case info.IsDir(): if err := os.MkdirAll(dst, info.Mode().Perm()); err != nil { return err } entries, err := os.ReadDir(src) if err != nil { return err } for _, entry := range entries { entryInfo, err := entry.Info() if err != nil { return err } if err := copyPathRecursive(filepath.Join(src, entry.Name()), filepath.Join(dst, entry.Name()), entryInfo, copied, total, onProgress); err != nil { return err } } return nil case info.Mode()&os.ModeSymlink != 0: target, err := os.Readlink(src) if err != nil { return err } return os.Symlink(target, dst) default: return copyFileWithProgress(src, dst, info, copied, total, onProgress) } } // copyFileWithProgress 复制单个普通文件,按读取量累计回调进度 func copyFileWithProgress(src, dst string, info os.FileInfo, copied *int64, total int64, onProgress func(copied, total int64)) error { in, err := os.Open(src) if err != nil { return err } defer in.Close() out, err := os.OpenFile(dst, os.O_CREATE|os.O_EXCL|os.O_WRONLY, info.Mode().Perm()) if err != nil { return err } defer out.Close() var reader io.Reader = in if onProgress != nil { reader = &common.CountingReader{R: in, OnN: func(n int64) { *copied += n onProgress(*copied, total) }} } _, err = io.Copy(out, reader) return err } // pathSize 统计路径总大小(文件为自身大小,目录递归求和,跳过符号链接) func pathSize(path string) (int64, error) { var total int64 err := filepath.WalkDir(path, func(_ string, d fs.DirEntry, err error) error { if err != nil { return err } if d.Type().IsRegular() { if info, e := d.Info(); e == nil { total += info.Size() } } return nil }) return total, err } // ========== ZIP操作接口 ========== // ListZipContents 列出ZIP文件内容(别名,保持向后兼容) func (s *FileSystemService) ListZipContents(zipPath string) ([]map[string]interface{}, error) { return ListZipContents(zipPath) } // ExtractFileFromZip 从ZIP提取文件内容(别名,保持向后兼容) func (s *FileSystemService) ExtractFileFromZip(zipPath, filePath string) (string, error) { return ExtractFileFromZip(zipPath, filePath) } // ExtractFileFromZipToTemp 从ZIP提取文件到临时目录(别名,保持向后兼容) func (s *FileSystemService) ExtractFileFromZipToTemp(zipPath, filePath string) (string, error) { return ExtractFileFromZipToTemp(zipPath, filePath) } // GetZipFileInfo 获取ZIP文件信息 func (s *FileSystemService) GetZipFileInfo(zipPath, filePath string) (map[string]interface{}, error) { return GetZipFileInfo(zipPath, filePath) } // ========== 辅助函数 ========== // getCurrentTimestamp 获取当前时间戳 func getCurrentTimestamp() time.Time { return time.Now() } // permanentDelete 永久删除路径(目录 RemoveAll / 文件 Remove)并记录删除日志 func (s *FileSystemService) permanentDelete(path string, info os.FileInfo) error { var deleteErr error if info.IsDir() { deleteErr = os.RemoveAll(path) } else { deleteErr = os.Remove(path) } s.logDelete(path, info.IsDir(), info.Size(), deleteErr) if deleteErr != nil { return fmt.Errorf("删除失败: %v", deleteErr) } return nil } // isInRecycleBin 检查路径是否在回收站中 func isInRecycleBin(path string) bool { recycleBinPath := filepath.Join(common.GetUserDataDir(), "recycle_bin") cleanPath := filepath.Clean(path) cleanBinPath := filepath.Clean(recycleBinPath) return strings.HasPrefix(cleanPath, cleanBinPath) } // ========== 辅助方法 ========== // validatePath 验证路径 func (s *FileSystemService) validatePath(path string) error { err := s.pathValidator.Validate(path) if err != nil && err.IsError { return err } return nil } // GetConfig 获取配置 func (s *FileSystemService) GetConfig() *Config { return s.config } // GetAuditLogger 获取审计日志记录器 func (s *FileSystemService) GetAuditLogger() *AuditLogger { return s.auditLogger } // GetRecycleBin 获取回收站 func (s *FileSystemService) GetRecycleBin() *RecycleBin { return s.recycleBin } // ========== 审计日志接口 ========== // logAudit 安全记录审计日志(自动处理 nil 检查) func (s *FileSystemService) logAudit(entry AuditLogEntry) { if s.auditLogger != nil { s.auditLogger.Log(entry) } } // logRead 记录读取操作审计日志 func (s *FileSystemService) logRead(path string, size int64, err error) { if s.auditLogger != nil { s.auditLogger.LogRead(path, size, err) } } // logWrite 记录写入操作审计日志 func (s *FileSystemService) logWrite(path string, size int64, err error) { if s.auditLogger != nil { s.auditLogger.LogWrite(path, size, err) } } // logDelete 记录删除操作审计日志 func (s *FileSystemService) logDelete(path string, isDir bool, size int64, err error) { if s.auditLogger != nil { s.auditLogger.LogDelete(path, isDir, size, err) } } // GetAuditLogs 获取审计日志 func (s *FileSystemService) GetAuditLogs(limit int) ([]map[string]interface{}, error) { if s.auditLogger == nil { return []map[string]interface{}{}, nil } logDir := filepath.Join(common.GetUserDataDir(), "logs") entries, err := GetRecentLogs(logDir, limit) if err != nil { return nil, err } result := make([]map[string]interface{}, len(entries)) for i, entry := range entries { result[i] = map[string]interface{}{ "timestamp": entry.Timestamp.Format("2006-01-02 15:04:05"), "operation": entry.Operation, "path": entry.Path, "size": entry.Size, "is_directory": entry.IsDirectory, "success": entry.Success, "error": entry.Error, } } return result, nil } // ========== 回收站接口 ========== // GetRecycleBinEntries 获取回收站条目 func (s *FileSystemService) GetRecycleBinEntries() ([]map[string]interface{}, error) { if s.recycleBin == nil { return []map[string]interface{}{}, nil } entries := s.recycleBin.ListEntries() result := make([]map[string]interface{}, len(entries)) for i, entry := range entries { result[i] = map[string]interface{}{ "original_path": entry.OriginalPath, "deleted_path": entry.DeletedPath, "deleted_time": entry.DeletedTime.Format("2006-01-02 15:04:05"), "size": entry.Size, "is_directory": entry.IsDirectory, } } return result, nil } // RestoreFromRecycleBin 从回收站恢复文件 func (s *FileSystemService) RestoreFromRecycleBin(recyclePath string) error { if s.recycleBin == nil { return fmt.Errorf("回收站未初始化") } return s.recycleBin.RestoreFromRecycleBin(recyclePath) } // DeletePermanently 永久删除回收站中的文件 func (s *FileSystemService) DeletePermanently(recyclePath string) error { if s.recycleBin == nil { return fmt.Errorf("回收站未初始化") } return s.recycleBin.DeletePermanently(recyclePath) } // EmptyRecycleBin 清空回收站 func (s *FileSystemService) EmptyRecycleBin() error { if s.recycleBin == nil { return fmt.Errorf("回收站未初始化") } return s.recycleBin.Empty() } // ResolveShortcut 解析快捷方式(.lnk)文件,返回目标路径 func (s *FileSystemService) ResolveShortcut(lnkPath string) (targetPath string, err error) { // 验证路径 if err := s.validatePath(lnkPath); err != nil { return "", fmt.Errorf("路径验证失败: %w", err) } // 检查文件扩展名 if filepath.Ext(lnkPath) != ".lnk" { return "", fmt.Errorf("不是快捷方式文件") } // 检查文件是否存在 if _, err := os.Stat(lnkPath); os.IsNotExist(err) { return "", fmt.Errorf("快捷方式文件不存在") } // 使用 Windows PowerShell 解析 lnk 文件 // 这种方法更可靠,不需要依赖第三方库 if runtime.GOOS == "windows" { // 创建 PowerShell 脚本 psScript := fmt.Sprintf( "$shell = New-Object -ComObject WScript.Shell; "+ "$shortcut = $shell.CreateShortcut('%s'); "+ "$shortcut.TargetPath", lnkPath, ) // 执行 PowerShell 命令 cmd := exec.Command("powershell", "-NoProfile", "-NonInteractive", "-Command", psScript) output, err := cmd.Output() if err != nil { return "", fmt.Errorf("解析快捷方式失败: %w", err) } // 去除空白字符 targetPath = string(output) targetPath = filepath.Clean(targetPath) // 如果目标路径为空,返回错误 if targetPath == "" || targetPath == "." { return "", fmt.Errorf("快捷方式目标路径为空") } return targetPath, nil } // 非 Windows 系统暂不支持 return "", fmt.Errorf("当前系统不支持快捷方式解析") } // Close 关闭服务,释放资源 func (s *FileSystemService) Close(ctx context.Context) error { s.mu.Lock() defer s.mu.Unlock() if !s.initialized { return nil } // 关闭审计日志 if s.auditLogger != nil { if err := s.auditLogger.Close(); err != nil { return fmt.Errorf("关闭审计日志失败: %w", err) } } s.initialized = false return nil } // ========== 全局服务实例(向后兼容)========== var ( globalService *FileSystemService globalServiceOnce sync.Once ) // GetGlobalService 获取全局文件系统服务实例(单例) // 保持向后兼容,但推荐使用依赖注入 func GetGlobalService() (*FileSystemService, error) { var initErr error globalServiceOnce.Do(func() { globalService, initErr = NewFileSystemService(DefaultConfig()) }) return globalService, initErr }