feat: batch resolve public bank conflicts

This commit is contained in:
Codex
2026-06-29 14:18:25 +08:00
parent 215cbd05f7
commit c00992aab5
13 changed files with 621 additions and 28 deletions

View File

@@ -15,6 +15,7 @@ import {
loadPublicQuestionBanks,
previewContentImport,
resolvePublicQuestionBankConflict,
resolvePublicQuestionBankConflicts,
runImportPostCheck,
syncPublicQuestionBank,
type ContentEntryAdminItem,
@@ -516,6 +517,43 @@ export default function TenantContentPage() {
}
}
async function resolveVisibleConflicts(resolution: 'accept_platform' | 'keep_local') {
const adoptionId = conflicts?.adoptionId;
const sourceQuestionIds = conflictItems(conflicts)
.map(item => item.sourceQuestionId || '')
.filter(Boolean)
.slice(0, 50);
if (!adoptionId || !sourceQuestionIds.length) {
setError('当前没有可批量处理的冲突。');
return;
}
const ok = await confirm(
resolution === 'accept_platform' ? '批量采纳平台版本' : '批量保留租户版本',
resolution === 'accept_platform'
? `确认批量用平台公共题库的新版本覆盖 ${sourceQuestionIds.length} 道租户副本?系统会逐条写入审计。`
: `确认批量保留 ${sourceQuestionIds.length} 道租户本地版本?后续同步不再反复提示同一版本。`,
);
if (!ok) return;
setBusy(`resolve-batch:${resolution}`);
setError('');
try {
await resolvePublicQuestionBankConflicts({
adoptionId,
sourceQuestionIds,
resolution,
limit: sourceQuestionIds.length,
});
const payload = await loadPublicQuestionBankConflicts(adoptionId);
setConflicts(payload.item || null);
Taro.showToast({ title: '批量处理完成', icon: 'success' });
reload();
} catch (nextError) {
setError(nextError instanceof Error ? nextError.message : '批量冲突处理失败');
} finally {
setBusy('');
}
}
const postCheckSummary = postCheck?.importPostCheck && typeof postCheck.importPostCheck === 'object'
? postCheck.importPostCheck as Record<string, unknown>
: postCheck;
@@ -711,6 +749,12 @@ export default function TenantContentPage() {
<Text className='admin-row-main'> {String(conflicts.status || conflicts.syncStatus || '-')}</Text>
<Text className='admin-row-meta'> {String(conflicts.conflictCount || 0)} · {String(conflicts.lastSyncedAt || '')}</Text>
<Text className='admin-row-meta'></Text>
{conflictItems(conflicts).length ? (
<View className='admin-row-actions'>
<Button className='admin-mini-button primary' loading={busy === 'resolve-batch:accept_platform'} onClick={() => resolveVisibleConflicts('accept_platform')}></Button>
<Button className='admin-mini-button' loading={busy === 'resolve-batch:keep_local'} onClick={() => resolveVisibleConflicts('keep_local')}></Button>
</View>
) : null}
</View>
{conflictItems(conflicts).slice(0, 20).map((item, index) => (
<View className='admin-row' key={`${item.sourceQuestionId || index}-${item.targetQuestionId || ''}`}>

View File

@@ -318,6 +318,18 @@ export async function resolvePublicQuestionBankConflict(input: { adoptionId: str
});
}
export async function resolvePublicQuestionBankConflicts(input: {
adoptionId: string;
sourceQuestionIds: string[];
resolution: 'accept_platform' | 'keep_local';
limit?: number;
}) {
return apiRequest<{ item?: Record<string, unknown> }>('/api/tenant-content/public-question-banks/conflicts/resolve-batch', {
method: 'POST',
body: input,
});
}
export async function runImportPostCheck(jobId: string) {
return apiRequest<{ item?: Record<string, unknown> }>('/api/tenant-content/imports/post-check', {
method: 'POST',