feat: 辅料功能、康正源拉取价格接口

This commit is contained in:
李琦
2026-09-30 17:02:54 +08:00
parent bfe50a8d67
commit 87c480f391
51 changed files with 4086 additions and 380 deletions

View File

@@ -0,0 +1,196 @@
/**
* 桌面打包脚本:交互确认版本号 → electron-builder → 复制友好文件名
*
* 交互:
* 上次打包版本号:x.y.z
* 1) 使用上次版本号
* 2) 输入新版本号
*
* 产物:
* - 自动更新用 ASCII 名:xk-desktop-{version}-win-x64.exe(latest.yml 引用)
* - 人工/后台上传用:萧康医药 {version}.exe
*
* 非交互(CI):XK_DESKTOP_VERSION=1.2.3 或 --version=1.2.3
*/
import { spawn } from 'node:child_process';
import { copyFileSync, existsSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
import { createInterface } from 'node:readline';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
const __dirname = dirname(fileURLToPath(import.meta.url));
const desktopRoot = join(__dirname, '..');
const packageJsonPath = join(desktopRoot, 'package.json');
const releaseDir = join(desktopRoot, 'release');
/** semver 简校验 */
function isValidVersion(v) {
return /^\d+\.\d+\.\d+([-+][0-9A-Za-z.-]+)?$/.test(v);
}
function readPkg() {
return JSON.parse(readFileSync(packageJsonPath, 'utf8'));
}
function writePkgVersion(version) {
const pkg = readPkg();
pkg.version = version;
writeFileSync(packageJsonPath, `${JSON.stringify(pkg, null, 2)}\n`, 'utf8');
}
/** 从 argv / 环境变量取强制版本(跳过交互) */
function getForcedVersion() {
const arg = process.argv.find((a) => a.startsWith('--version='));
if (arg) return arg.slice('--version='.length).trim();
if (process.env.XK_DESKTOP_VERSION) return String(process.env.XK_DESKTOP_VERSION).trim();
return '';
}
function ask(question) {
const rl = createInterface({ input: process.stdin, output: process.stdout });
return new Promise((resolve) => {
rl.question(question, (answer) => {
rl.close();
resolve(String(answer || '').trim());
});
});
}
/**
* 交互确认本次打包版本
* @returns {Promise<string>}
*/
async function resolveVersion() {
const forced = getForcedVersion();
if (forced) {
if (!isValidVersion(forced)) {
throw new Error(`无效版本号: ${forced}(请用如 1.0.1)`);
}
console.log(`使用指定版本号: ${forced}`);
return forced;
}
if (!process.stdin.isTTY) {
const current = readPkg().version || '1.0.0';
console.log(`非交互环境,沿用 package.json 版本: ${current}`);
return current;
}
const last = readPkg().version || '1.0.0';
console.log('');
console.log('========== 桌面端版本确认 ==========');
console.log(`上次打包版本号:${last}`);
console.log('请选择:');
console.log(` 1) 使用上次版本号(${last})`);
console.log(' 2) 输入新的版本号');
console.log('');
let choice = await ask('请输入选项 [1/2](默认 1): ');
if (choice === '' || choice === '1') {
return last;
}
if (choice !== '2') {
console.log('未识别选项,默认使用上次版本号');
return last;
}
for (;;) {
const next = await ask('请输入新版本号(如 1.0.1): ');
if (isValidVersion(next)) {
return next;
}
console.log('格式不正确,请使用 semver,例如 1.0.1');
}
}
function runBuilder() {
// electron-builder 下载 Electron / NSIS 等会打 GitHub;国内网络常超时。
// 根 .npmrc 有镜像配置,但 spawn 出的进程不一定带上,这里强制写入环境变量。
const electronMirror =
process.env.ELECTRON_MIRROR || 'https://npmmirror.com/mirrors/electron/';
const builderBinariesMirror =
process.env.ELECTRON_BUILDER_BINARIES_MIRROR ||
'https://npmmirror.com/mirrors/electron-builder-binaries/';
const env = {
...process.env,
ELECTRON_MIRROR: electronMirror,
ELECTRON_BUILDER_BINARIES_MIRROR: builderBinariesMirror,
// 兼容 app-builder-lib 读取的 npm_config_* 形态
npm_config_electron_mirror: electronMirror,
npm_config_electron_builder_binaries_mirror: builderBinariesMirror,
};
console.log(`Electron 镜像: ${electronMirror}`);
console.log(`Builder 工具链镜像: ${builderBinariesMirror}`);
return new Promise((resolve, reject) => {
const child = spawn(
'pnpm exec electron-builder --config electron-builder.config.cjs',
{
cwd: desktopRoot,
env,
shell: true,
stdio: 'inherit',
},
);
child.on('error', reject);
child.on('close', (code) => {
if (code === 0) resolve();
else reject(new Error(`electron-builder 失败,退出码 ${code}`));
});
});
}
/**
* 复制 Windows 安装包为「萧康医药 {version}.exe」方便后台/人工上传
* 保留原 ASCII 文件名供 electron-updater 的 latest.yml 使用
*/
function copyFriendlyWinExe(version) {
if (!existsSync(releaseDir)) {
console.warn(`未找到 release 目录: ${releaseDir}`);
return;
}
const files = readdirSync(releaseDir);
// 优先精确匹配 builder artifactName
const exact = `xk-desktop-${version}-win-x64.exe`;
let src = files.includes(exact) ? exact : '';
if (!src) {
src =
files.find(
(f) =>
f.endsWith('.exe') &&
f.includes(version) &&
!f.includes('萧康') &&
!f.toLowerCase().includes('blockmap'),
) || '';
}
if (!src) {
console.warn('未找到 Windows 安装包,跳过友好文件名复制');
return;
}
const destName = `萧康医药 ${version}.exe`;
const from = join(releaseDir, src);
const to = join(releaseDir, destName);
copyFileSync(from, to);
console.log(`\n已生成上传用安装包: ${to}`);
console.log(`(自动更新仍使用: ${from})`);
}
async function main() {
const version = await resolveVersion();
const current = readPkg().version;
if (version !== current) {
writePkgVersion(version);
console.log(`已写入 package.json version = ${version}`);
} else {
console.log(`版本号保持 ${version}`);
}
console.log('\n开始 electron-builder 打包…\n');
await runBuilder();
copyFriendlyWinExe(version);
console.log('\n打包流程结束');
}
main().catch((err) => {
console.error(`\n❌ ${err.message || err}`);
process.exit(1);
});

View File

@@ -0,0 +1,44 @@
/**
* 直接跑 electron-builder(跳过版本交互),并注入国内镜像环境变量
* 避免打 GitHub 超时:connect ETIMEDOUT 20.x.x.x:443
*/
import { spawn } from 'node:child_process';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
const desktopRoot = join(dirname(fileURLToPath(import.meta.url)), '..');
const electronMirror =
process.env.ELECTRON_MIRROR || 'https://npmmirror.com/mirrors/electron/';
const builderBinariesMirror =
process.env.ELECTRON_BUILDER_BINARIES_MIRROR ||
'https://npmmirror.com/mirrors/electron-builder-binaries/';
const env = {
...process.env,
ELECTRON_MIRROR: electronMirror,
ELECTRON_BUILDER_BINARIES_MIRROR: builderBinariesMirror,
npm_config_electron_mirror: electronMirror,
npm_config_electron_builder_binaries_mirror: builderBinariesMirror,
};
console.log(`Electron 镜像: ${electronMirror}`);
console.log(`Builder 工具链镜像: ${builderBinariesMirror}`);
const child = spawn(
'pnpm exec electron-builder --config electron-builder.config.cjs',
{
cwd: desktopRoot,
env,
shell: true,
stdio: 'inherit',
},
);
child.on('close', (code) => {
process.exit(code ?? 1);
});
child.on('error', (err) => {
console.error(err);
process.exit(1);
});