feat: 重构更新流程 — 无更新不抛异常、用户决定下载、更新详情展示(md文件中的)
【核心重构】
- getLatestVersion() 不再 throw Error,无更新时返回 { version: APP_VERSION }
- checkForUpdates() 先自行预检 → 无更新直发 IPC,有更新才交 electron-updater
- 移除 NO_UPDATE_MESSAGE 常量及所有字符串匹配拦截代码
- HeiXiuProvider 增加 500ms TTL 缓存,避免预检+electron-updater 重复请求 API
【用户体验】
- 检查到新版本后不再自动下载,展示更新内容供用户决定
- 新增"下载更新"按钮(与"安装更新"分离为两步操作)
- 设置页新增"查看详情"弹窗,Markdown 格式化渲染更新日志
- available / downloading / downloaded 三个状态 UI 独立展示
【Bug 修复】
- 测试服务器版本排序:字符串序 → 语义版本序(_version_key),0.0.10 正确 > 0.0.9
- 版本比较:!= → <(_version_key),防止高版本误判为有更新
- get_best_release fallback 同样改为语义版本排序
- Windows cmd set 命令尾部空格 → Invalid URL(.trim() 修复)
- electron-updater 'error' 事件中拦截 NO_UPDATE_MESSAGE → UPDATE_NOT_AVAILABLE
【文档更新】
- CHANGELOG.md、PROJECT.md、UpdateA.md 同步更新架构图和流程说明
This commit is contained in:
@@ -63,19 +63,21 @@ const DEFAULT_TIMEOUT = 30_000;
|
||||
|
||||
/** 拼接 URL(baseURL + path + query) */
|
||||
function buildUrl(url: string, config: RequestConfig): string {
|
||||
let fullUrl = url;
|
||||
|
||||
if (config.baseURL) {
|
||||
// url 以 / 开头时为绝对路径,否则拼接
|
||||
const base = config.baseURL.replace(/\/+$/, '');
|
||||
const path = url.startsWith('/') ? url : `/${url}`;
|
||||
fullUrl = `${base}${path}`;
|
||||
// 如果没有 baseURL 且 url 不是绝对地址,直接报错(便于定位问题)
|
||||
if (!config.baseURL && !/^https?:\/\//i.test(url)) {
|
||||
throw new RequestError(
|
||||
`URL 缺少 baseURL,无法构造绝对地址。url="${url}", config=${JSON.stringify({ ...config, params: config.params ? '[object]' : undefined })}`,
|
||||
);
|
||||
}
|
||||
|
||||
const base = (config.baseURL || '').trim().replace(/\/+$/, '');
|
||||
const path = url.startsWith('/') ? url : `/${url}`;
|
||||
let fullUrl = base ? `${base}${path}` : url;
|
||||
|
||||
if (config.params) {
|
||||
const search = new URLSearchParams();
|
||||
for (const [key, value] of Object.entries(config.params)) {
|
||||
if (value !== undefined) {
|
||||
if (value !== undefined && value !== null) {
|
||||
search.append(key, String(value));
|
||||
}
|
||||
}
|
||||
@@ -85,6 +87,16 @@ function buildUrl(url: string, config: RequestConfig): string {
|
||||
}
|
||||
}
|
||||
|
||||
// 调试:在 net.request 调用之前验证 URL 合法性
|
||||
try {
|
||||
void new URL(fullUrl);
|
||||
} catch {
|
||||
throw new RequestError(
|
||||
`构造的 URL 无法通过 Node.js 校验: "${fullUrl}"`,
|
||||
);
|
||||
}
|
||||
|
||||
console.log('[net-request] buildUrl:', fullUrl);
|
||||
return fullUrl;
|
||||
}
|
||||
|
||||
@@ -112,6 +124,17 @@ export async function request<T = unknown>(
|
||||
const timeout = config.timeout ?? DEFAULT_TIMEOUT;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
// 调试:验证 URL 是否合法
|
||||
try {
|
||||
void new URL(fullUrl);
|
||||
} catch (urlErr) {
|
||||
console.error('[net-request] 非法 URL:', JSON.stringify(fullUrl));
|
||||
console.error('[net-request] URL 字符码:', [...fullUrl].map(c => c.charCodeAt(0)).join(','));
|
||||
console.error('[net-request] config:', JSON.stringify(config));
|
||||
reject(new RequestError(`非法 URL: ${(urlErr as Error).message}`));
|
||||
return;
|
||||
}
|
||||
|
||||
const req = net.request({
|
||||
method,
|
||||
url: fullUrl,
|
||||
|
||||
Reference in New Issue
Block a user