🛠️ chore(ratio-sync): improve upstream ratio comparison & output cleanliness

Summary
1. Consider “both unset” as identical
   • When both localValue and upstreamValue are nil, mark upstreamValue as "same" to avoid showing “Not set”.

2. Exclude fully-synced upstream channels from result
   • Scan `differences` to detect channels that contain at least one divergent value.
   • Remove channels whose every ratio is either `"same"` or `nil`, so the frontend only receives actionable discrepancies.

Why
These changes reduce visual noise in the Upstream Ratio Sync table, making it easier for admins to focus on models requiring attention. No functional regressions or breaking API changes are introduced.
This commit is contained in:
Apple\Apple
2025-06-19 18:38:43 +08:00
parent 67546f4b2a
commit a9f98c5d39
3 changed files with 74 additions and 61 deletions

View File

@@ -11,7 +11,7 @@ import {
RefreshCcw,
CheckSquare,
} from 'lucide-react';
import { API, showError, showSuccess, showWarning } from '../../../helpers';
import { API, showError, showSuccess, showWarning, stringToColor } from '../../../helpers';
import { DEFAULT_ENDPOINT } from '../../../constants';
import { useTranslation } from 'react-i18next';
import {
@@ -35,14 +35,12 @@ export default function UpstreamRatioSync(props) {
// 差异数据和测试结果
const [differences, setDifferences] = useState({});
const [testResults, setTestResults] = useState([]);
const [resolutions, setResolutions] = useState({});
// 分页相关状态
const [currentPage, setCurrentPage] = useState(1);
const [pageSize, setPageSize] = useState(10);
// 获取所有渠道
const fetchAllChannels = async () => {
setLoading(true);
try {
@@ -51,18 +49,16 @@ export default function UpstreamRatioSync(props) {
if (res.data.success) {
const channels = res.data.data || [];
// 转换为Transfer组件所需格式
const transferData = channels.map(channel => ({
key: channel.id,
label: channel.name,
value: channel.id,
disabled: false, // 所有渠道都可以选择
disabled: false,
_originalData: channel,
}));
setAllChannels(transferData);
// 初始化端点配置
const initialEndpoints = {};
transferData.forEach(channel => {
initialEndpoints[channel.key] = DEFAULT_ENDPOINT;
@@ -78,7 +74,6 @@ export default function UpstreamRatioSync(props) {
}
};
// 确认选择渠道
const confirmChannelSelection = () => {
const selected = allChannels
.filter(ch => selectedChannelIds.includes(ch.value))
@@ -93,7 +88,6 @@ export default function UpstreamRatioSync(props) {
fetchRatiosFromChannels(selected);
};
// 从选定渠道获取倍率
const fetchRatiosFromChannels = async (channelList) => {
setSyncLoading(true);
@@ -113,17 +107,14 @@ export default function UpstreamRatioSync(props) {
const { differences = {}, test_results = [] } = res.data.data;
// 显示测试结果
const errorResults = test_results.filter(r => r.status === 'error');
if (errorResults.length > 0) {
showWarning(t('部分渠道测试失败:') + errorResults.map(r => `${r.name}: ${r.error}`).join(', '));
}
setDifferences(differences);
setTestResults(test_results);
setResolutions({});
// 判断是否有差异
if (Object.keys(differences).length === 0) {
showSuccess(t('已与上游倍率完全一致,无需同步'));
}
@@ -134,7 +125,6 @@ export default function UpstreamRatioSync(props) {
}
};
// 解决冲突/选择值
const selectValue = (model, ratioType, value) => {
setResolutions(prev => ({
...prev,
@@ -145,7 +135,6 @@ export default function UpstreamRatioSync(props) {
}));
};
// 应用同步
const applySync = async () => {
const currentRatios = {
ModelRatio: JSON.parse(props.options.ModelRatio || '{}'),
@@ -154,7 +143,6 @@ export default function UpstreamRatioSync(props) {
ModelPrice: JSON.parse(props.options.ModelPrice || '{}'),
};
// 应用已选择的值
Object.entries(resolutions).forEach(([model, ratios]) => {
Object.entries(ratios).forEach(([ratioType, value]) => {
const optionKey = ratioType
@@ -165,7 +153,6 @@ export default function UpstreamRatioSync(props) {
});
});
// 保存到后端
setLoading(true);
try {
const updates = Object.entries(currentRatios).map(([key, value]) =>
@@ -180,11 +167,26 @@ export default function UpstreamRatioSync(props) {
if (results.every(res => res.data.success)) {
showSuccess(t('同步成功'));
props.refresh();
// 清空状态
setDifferences({});
setTestResults([]);
setDifferences(prevDifferences => {
const newDifferences = { ...prevDifferences };
Object.entries(resolutions).forEach(([model, ratios]) => {
Object.keys(ratios).forEach(ratioType => {
if (newDifferences[model] && newDifferences[model][ratioType]) {
delete newDifferences[model][ratioType];
if (Object.keys(newDifferences[model]).length === 0) {
delete newDifferences[model];
}
}
});
});
return newDifferences;
});
setResolutions({});
setSelectedChannelIds([]);
} else {
showError(t('部分保存失败'));
}
@@ -195,14 +197,12 @@ export default function UpstreamRatioSync(props) {
}
};
// 计算当前页显示的数据
const getCurrentPageData = (dataSource) => {
const startIndex = (currentPage - 1) * pageSize;
const endIndex = startIndex + pageSize;
return dataSource.slice(startIndex, endIndex);
};
// 渲染表格头部
const renderHeader = () => (
<div className="flex flex-col w-full">
<div className="flex flex-col md:flex-row justify-between items-center gap-4 w-full">
@@ -219,7 +219,6 @@ export default function UpstreamRatioSync(props) {
</Button>
{(() => {
// 检查是否有选择可应用的值
const hasSelections = Object.keys(resolutions).length > 0;
return (
@@ -239,9 +238,7 @@ export default function UpstreamRatioSync(props) {
</div>
);
// 渲染差异表格
const renderDifferenceTable = () => {
// 构建数据源
const dataSource = useMemo(() => {
const tmp = [];
@@ -260,7 +257,6 @@ export default function UpstreamRatioSync(props) {
return tmp;
}, [differences]);
// 收集所有上游渠道名称
const upstreamNames = useMemo(() => {
const set = new Set();
dataSource.forEach((row) => {
@@ -274,13 +270,12 @@ export default function UpstreamRatioSync(props) {
<Empty
image={<IllustrationNoResult style={{ width: 150, height: 150 }} />}
darkModeImage={<IllustrationNoResultDark style={{ width: 150, height: 150 }} />}
description={Object.keys(differences).length === 0 ? t('已与上游倍率完全一致') : t('请先选择同步渠道')}
description={Object.keys(differences).length === 0 ? t('暂无差异化倍率显示') : t('请先选择同步渠道')}
style={{ padding: 30 }}
/>
);
}
// 列定义
const columns = [
{
title: t('模型'),
@@ -297,7 +292,7 @@ export default function UpstreamRatioSync(props) {
cache_ratio: t('缓存倍率'),
model_price: t('固定价格'),
};
return <Tag shape="circle">{typeMap[text] || text}</Tag>;
return <Tag color={stringToColor(text)} shape="circle">{typeMap[text] || text}</Tag>;
},
},
{
@@ -309,16 +304,13 @@ export default function UpstreamRatioSync(props) {
</Tag>
),
},
// 动态上游列
...upstreamNames.map((upName) => {
// 计算该渠道的全选状态
const channelStats = (() => {
let selectableCount = 0; // 可选择的项目数量
let selectedCount = 0; // 已选择的项目数量
let selectableCount = 0;
let selectedCount = 0;
dataSource.forEach((row) => {
const upstreamVal = row.upstreams?.[upName];
// 只有具体数值的才是可选择的不是null、undefined或"same"
if (upstreamVal !== null && upstreamVal !== undefined && upstreamVal !== 'same') {
selectableCount++;
const isSelected = resolutions[row.model]?.[row.ratioType] === upstreamVal;
@@ -337,7 +329,6 @@ export default function UpstreamRatioSync(props) {
};
})();
// 处理全选/取消全选
const handleBulkSelect = (checked) => {
setResolutions((prev) => {
const newRes = { ...prev };
@@ -346,11 +337,9 @@ export default function UpstreamRatioSync(props) {
const upstreamVal = row.upstreams?.[upName];
if (upstreamVal !== null && upstreamVal !== undefined && upstreamVal !== 'same') {
if (checked) {
// 选择该值
if (!newRes[row.model]) newRes[row.model] = {};
newRes[row.model][row.ratioType] = upstreamVal;
} else {
// 取消选择该值
if (newRes[row.model]) {
delete newRes[row.model][row.ratioType];
if (Object.keys(newRes[row.model]).length === 0) {
@@ -389,7 +378,6 @@ export default function UpstreamRatioSync(props) {
return <Tag color="blue" shape="circle">{t('与本地相同')}</Tag>;
}
// 有具体值,可以选择
const isSelected = resolutions[record.model]?.[record.ratioType] === upstreamVal;
return (
@@ -454,7 +442,6 @@ export default function UpstreamRatioSync(props) {
);
};
// 更新渠道端点
const updateChannelEndpoint = useCallback((channelId, endpoint) => {
setChannelEndpoints(prev => ({ ...prev, [channelId]: endpoint }));
}, []);