修复: 粘贴图失效与SFTP串台回退等一批问题

粘贴图根修(Ctrl+V直读系统剪贴板,不再依赖不可靠的paste事件)/SFTP串台双刀(拨号期禁回退本机+盘符路径自动回根)/PDF导出目录设置与文案带落盘路径/性能批(连接保存下载防重/md渲染防抖+预热/hljs按需加载/审计锁外置/快捷方式解析超时/webview-watch可停)/前端62类型错清零(含keyPath蛇形字段炸弹)/目录图标字段错位修复
This commit is contained in:
lxy
2026-10-24 13:48:54 +08:00
parent 4675ae381d
commit 75a42c0b6a
48 changed files with 1072 additions and 220 deletions
+89
View File
@@ -0,0 +1,89 @@
package common
import (
"fmt"
"io"
"log/slog"
"os"
"path/filepath"
"sync"
"time"
)
// GetLogsDir 日志目录:~/.u-desk/logs(audit 日志同目录)
func GetLogsDir() string {
return filepath.Join(GetUserDataDir(), "logs")
}
// dailyFile 按天滚动的追加写文件:每次写时发现日期变化即切换新文件。
// 只做按天滚动,不搞大小上限/压缩/清理,崩溃取证够用。
type dailyFile struct {
mu sync.Mutex
dir string
naming func(t time.Time) string
f *os.File
day string
}
func newDailyFile(dir string, naming func(time.Time) string) *dailyFile {
return &dailyFile{dir: dir, naming: naming}
}
func (w *dailyFile) Write(p []byte) (int, error) {
w.mu.Lock()
defer w.mu.Unlock()
now := time.Now()
day := now.Format("2006-01-02")
if w.f != nil && day != w.day {
w.f.Close()
w.f = nil
}
if w.f == nil {
if err := os.MkdirAll(w.dir, 0755); err != nil {
return 0, err
}
f, err := os.OpenFile(filepath.Join(w.dir, w.naming(now)), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
return 0, err
}
w.f, w.day = f, day
}
return w.f.Write(p)
}
func (w *dailyFile) Close() error {
w.mu.Lock()
defer w.mu.Unlock()
if w.f == nil {
return nil
}
err := w.f.Close()
w.f = nil
return err
}
// FileLogOut 应用文件日志的底层写出器(stderr + 按天文件 MultiWriter)。
// SetupFileLogging 之后有效;供需要独立过滤级别的 logger 复用同一落盘目的地
// (如 wails 系统日志降到 Warn,业务 slog.Default() 保持 INFO 全量)。
var FileLogOut io.Writer = os.Stderr
// SetupFileLogging slog 双路落盘:stderr + ~/.u-desk/logs/app-YYYY-MM-DD.log。
// windowsgui 下 stderr 被丢弃,文件路是日志唯一可见载体;
// 文件侧打不开时静默降级为仅 stderr,不因日志失败阻断启动。
func SetupFileLogging() {
out := io.Writer(os.Stderr)
w := newDailyFile(GetLogsDir(), func(t time.Time) string {
return fmt.Sprintf("app-%s.log", t.Format("2006-01-02"))
})
// 预开当天文件探测文件侧可用性(Write 内部含懒开/换天逻辑)
if _, err := w.Write(nil); err == nil {
// 文件必须放 MultiWriter 首位:windowsgui 下 stderr 句柄无效,
// MultiWriter 按序短路,stderr 在前会吞掉后续文件路(真机零日志的根因)
out = io.MultiWriter(w, os.Stderr)
} else {
w.Close()
}
FileLogOut = out
slog.SetDefault(slog.New(slog.NewTextHandler(out, nil)))
}