Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/build-packages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on:
push:
branches:
- main
- win7-compat-logging
workflow_dispatch:

env:
Expand All @@ -12,6 +13,7 @@ env:
jobs:
macos:
name: macOS packages
if: github.ref_name == 'main'
runs-on: macos-latest
env:
CSC_IDENTITY_AUTO_DISCOVERY: "false"
Expand Down Expand Up @@ -84,7 +86,7 @@ jobs:
- name: Upload Windows artifacts
uses: actions/upload-artifact@v4
with:
name: gold-dashboard-windows
name: ${{ github.ref_name == 'main' && 'gold-dashboard-windows' || 'gold-dashboard-windows-win7' }}
path: |
release/*.exe
release/*.blockmap
Expand Down
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

一个基于 Electron + React + Vite 的桌面实时金价面板,适配 macOS 和 Windows。

当前分支是 Windows 7 兼容分支,使用 Electron 22。`main` 分支继续使用最新 Electron,用于 macOS 和现代 Windows 系统迭代。

## 功能

- 默认每 10 秒刷新浙商银行积存金价格
- 默认每 3 秒刷新浙商银行积存金价格
- 可在浙商银行 / 新浪两个 API 源之间切换
- 涨红色 `▲`,跌绿色 `▼`,持平灰色 `—`
- 无系统标题栏,窗口始终置顶,正常窗口全区域可拖拽
Expand Down Expand Up @@ -43,3 +45,10 @@ npm run dist
- 新浪行情:`https://hq.sinajs.cn/list=SGE_AU9999`

这些接口均由主进程请求,渲染进程只接收标准化后的行情数据。

## 本地日志

应用会把启动错误、窗口加载失败、接口请求失败和渲染进程异常写入本地日志,方便排查打不开的问题。

- Windows 打包版:`%APPDATA%\实时金价面板\gold-dashboard.log`
- macOS 打包版:`~/Library/Application Support/实时金价面板/gold-dashboard.log`
89 changes: 89 additions & 0 deletions electron/logger.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';

const LOG_FILE_NAME = 'gold-dashboard.log';
const FALLBACK_APP_NAME = '实时金价面板';

export function createFileLogger({ getDirectory, fileName = LOG_FILE_NAME, now = () => new Date() } = {}) {
let cachedPath;

function getLogFilePath() {
if (cachedPath) {
return cachedPath;
}

const directory = resolveLogDirectory(getDirectory);
fs.mkdirSync(directory, { recursive: true });
cachedPath = path.join(directory, fileName);
return cachedPath;
}

function write(level, message, details) {
const suffix = details === undefined ? '' : ` ${formatDetails(details)}`;
const line = `[${now().toISOString()}] [${level}] ${message}${suffix}\n`;

try {
fs.appendFileSync(getLogFilePath(), line, 'utf8');
} catch (error) {
console.error('Failed to write gold dashboard log:', error);
console.error(line.trimEnd());
}
}

return {
get path() {
return getLogFilePath();
},
info: (message, details) => write('INFO', message, details),
warn: (message, details) => write('WARN', message, details),
error: (message, details) => write('ERROR', message, details),
};
}

export function serializeError(error) {
if (error instanceof Error) {
return {
name: error.name,
message: error.message,
stack: error.stack,
code: error.code,
};
}

return error;
}

function resolveLogDirectory(getDirectory) {
try {
const directory = getDirectory?.();
if (directory) {
return directory;
}
} catch {
// Fall back below; logging must not block app startup.
}

const home = os.homedir();

if (process.platform === 'win32') {
return path.join(process.env.APPDATA || process.env.LOCALAPPDATA || home || process.cwd(), FALLBACK_APP_NAME);
}

if (process.platform === 'darwin') {
return path.join(home || process.cwd(), 'Library', 'Application Support', FALLBACK_APP_NAME);
}

return path.join(process.env.XDG_CONFIG_HOME || path.join(home || process.cwd(), '.config'), FALLBACK_APP_NAME);
}

function formatDetails(details) {
try {
return JSON.stringify(details, (_key, value) => serializeError(value));
} catch (error) {
return JSON.stringify({
details: String(details),
stringifyError: serializeError(error),
});
}
}
Loading
Loading