Files
MyGoNavi/internal/app/methods_import_artifacts.go
Syngnat 390a249105 feat(data-import): 重构大文件导入与安全任务运行时
- SQL、CSV、JSON 与 XLSX 改为资源受限的流式解析,并支持压缩、编码和方言预检

- 增加遇错停止/继续策略、持久任务、错误行导出、源文件身份与安全取消

- 为数据库驱动补齐上下文事务和写入结果未知语义,避免失败重放与脏会话复用
2026-08-08 20:51:23 +08:00

76 lines
2.4 KiB
Go

package app
import (
"io"
"path/filepath"
"strings"
"GoNavi-Wails/internal/connection"
"github.com/wailsapp/wails/v2/pkg/runtime"
)
func (a *App) ensureImportErrorArtifactStore() (*importErrorArtifactStore, error) {
a.importArtifactMu.Lock()
defer a.importArtifactMu.Unlock()
if a.importErrorArtifacts != nil {
return a.importErrorArtifacts, nil
}
configDir := strings.TrimSpace(a.configDir)
if configDir == "" {
configDir = resolveAppConfigDir()
}
store, err := newImportErrorArtifactStore(filepath.Join(configDir, "import-artifacts"))
if err != nil {
return nil, err
}
a.importErrorArtifacts = store
return store, nil
}
// ExportImportErrorRows copies a managed rejected-row artifact to a path the
// desktop user explicitly selected. The opaque ID prevents arbitrary local
// files from being read through the RPC boundary.
func (a *App) ExportImportErrorRows(artifactID string) connection.QueryResult {
store, err := a.ensureImportErrorArtifactStore()
if err != nil {
return connection.QueryResult{Success: false, Message: err.Error()}
}
source, err := store.Open(artifactID)
if err != nil {
return connection.QueryResult{Success: false, Message: a.appText("file.backend.error.import_error_artifact_not_found", nil)}
}
defer source.Close()
targetPath, err := a.showSaveFileDialog(runtime.SaveDialogOptions{
Title: a.appText("file.backend.dialog.export_import_errors", nil),
DefaultFilename: "gonavi-import-errors.jsonl",
Filters: []runtime.FileFilter{{
DisplayName: "JSON Lines (*.jsonl)",
Pattern: "*.jsonl",
}},
})
if err != nil {
return connection.QueryResult{Success: false, Message: err.Error()}
}
if strings.TrimSpace(targetPath) == "" {
return connection.QueryResult{Success: false, Message: a.appText("file.backend.message.user_cancelled", nil)}
}
target, err := createAtomicExportTarget(targetPath)
if err != nil {
return connection.QueryResult{Success: false, Message: err.Error()}
}
defer target.abort()
if _, err := io.Copy(target.file, source); err != nil {
return connection.QueryResult{Success: false, Message: err.Error()}
}
if err := target.commit(); err != nil {
return connection.QueryResult{Success: false, Message: err.Error()}
}
return connection.QueryResult{
Success: true,
Data: map[string]interface{}{"filePath": targetPath},
Message: a.appText("file.backend.message.import_errors_exported", nil),
}
}