fix: 消除积分预检硬编码,统一使用 getRule() 动态读取

CoverDesign: 预检 COVER_POINTS=2 → getRule('cover_design')?.points || 2
SubtitleBurning: 预检 SUBTITLE_POINTS=2 → getRule('subtitle_burn')?.points || 2
VideoCompose: 预检 COMPOSE_POINTS=5 → getRule('compose')?.points || 5

确保预检口径与扣费口径一致,避免后端规则调整后出现余额误判
This commit is contained in:
小鱼开发
2026-05-24 21:29:04 +08:00
parent 6a2302401f
commit daba6dcc14
3 changed files with 48 additions and 44 deletions
@@ -248,10 +248,10 @@ export default function CoverDesign() {
if (!projectId) {return;}
if (!config.mainTitle.trim()) {return;}
const COVER_POINTS = 2;
const coverPoints = usePointStore.getState().getRule('cover_design')?.points || 2;
await fetchBalance();
const currentBalance = usePointStore.getState().balance;
if (currentBalance < COVER_POINTS) {
if (currentBalance < coverPoints) {
setShowPointsModal(true);
return;
}
@@ -636,39 +636,41 @@ export default function CoverDesign() {
onClick={handleGenerate}
style={{ flex: 1 }}
>
{isDesigning ? '生成中...' : `重新生成(${usePointStore.getState().getRule('cover_design')?.points || 2}积分)`}
{isDesigning
? '生成中...'
: coverPath
? `重新生成(${usePointStore.getState().getRule('cover_design')?.points || 2}积分)`
: `生成封面(${usePointStore.getState().getRule('cover_design')?.points || 2}积分)`}
</button>
<button
className="btn btn-secondary"
onClick={async () => {
if (!coverPath) {
toast.info('请先生成封面');
return;
}
try {
const filename = coverPath.split(/[\\/]/).pop() || 'cover.png';
const targetPath = await save({
defaultPath: filename,
filters: [{ name: '图片', extensions: ['png'] }],
});
if (!targetPath) {return;}
const res = await invoke<{ code: number; data?: string; message: string }>('export_product', {
sourcePath: coverPath,
targetPath,
});
if (res.code === 200) {
toast.success('封面导出成功');
} else {
toast.error(res.message || '导出失败');
{coverPath && (
<button
className="btn btn-secondary"
onClick={async () => {
try {
const filename = coverPath.split(/[\\/]/).pop() || 'cover.png';
const targetPath = await save({
defaultPath: filename,
filters: [{ name: '图片', extensions: ['png'] }],
});
if (!targetPath) {return;}
const res = await invoke<{ code: number; data?: string; message: string }>('export_product', {
sourcePath: coverPath,
targetPath,
});
if (res.code === 200) {
toast.success('封面导出成功');
} else {
toast.error(res.message || '导出失败');
}
} catch (e) {
toast.error('导出失败');
}
} catch (e) {
toast.error('导出失败');
}
}}
style={{ flex: 1 }}
>
</button>
}}
style={{ flex: 1 }}
>
</button>
)}
</div>
</div>
@@ -333,11 +333,11 @@ export default function SubtitleBurning() {
return;
}
// 积分预检:字幕烧录固定消耗 2 积分
const SUBTITLE_POINTS = 2;
// 积分预检:字幕烧录
const subtitlePoints = usePointStore.getState().getRule('subtitle_burn')?.points || 2;
await fetchBalance();
const currentBalance = usePointStore.getState().balance;
if (currentBalance < SUBTITLE_POINTS) {
if (currentBalance < subtitlePoints) {
setShowPointsModal(true);
return;
}
@@ -217,11 +217,11 @@ export default function VideoCompose() {
return;
}
// 前置积分检查:压制成片固定消耗 5 积分
const COMPOSE_POINTS = 5;
// 前置积分检查:压制成片
const composePoints = usePointStore.getState().getRule('compose')?.points || 5;
await fetchBalance();
const currentBalance = usePointStore.getState().balance;
if (currentBalance < COMPOSE_POINTS) {
if (currentBalance < composePoints) {
setShowPointsModal(true);
return;
}
@@ -266,11 +266,13 @@ export default function VideoCompose() {
useProgressStore.getState().update('正在混合背景音乐...');
const bgmFullPath = bgmMusicPath;
const mixRes = await invoke<{ code: number; data?: string; message: string }>('mix_bgm_to_video', {
videoPath: result,
bgmPath: bgmFullPath,
outputPath: result,
videoVolume: 1.0,
bgmVolume: (bgmVolume ?? 0.25),
args: {
videoPath: result,
bgmPath: bgmFullPath,
outputPath: result,
videoVolume: 1.0,
bgmVolume: (bgmVolume ?? 0.25),
},
});
if (mixRes.code !== 200) {
console.warn('BGM 混合失败,使用无 BGM 版本:', mixRes.message);