mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-08-15 19:24:23 +08:00
- SQL、CSV、JSON 与 XLSX 改为资源受限的流式解析,并支持压缩、编码和方言预检 - 增加遇错停止/继续策略、持久任务、错误行导出、源文件身份与安全取消 - 为数据库驱动补齐上下文事务和写入结果未知语义,避免失败重放与脏会话复用
76 lines
2.4 KiB
Go
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),
|
|
}
|
|
}
|