fix(agent): 修复规则中心表格版本和修改记录

补齐规则资产 JSON 读写接口和前端调用,修复 AuditView 导入缺失。

Excel 在线编辑改为比对所有页签并生成最近修改记录,版本快照统一保存到 rules/finance-rules/.versions。

隔离规则表测试存储,避免测试或旧入口写入真实规则目录与 storage/agent_assets。
This commit is contained in:
caoxiaozhu
2026-05-19 15:41:53 +00:00
parent 9472813739
commit d460ee0fe7
13 changed files with 782 additions and 167 deletions

View File

@@ -136,6 +136,18 @@ export function fetchAgentAssetVersionTimeline(assetId) {
return apiRequest(`/agent-assets/${assetId}/version-timeline`)
}
export function fetchAgentAssetRuleJson(assetId) {
return apiRequest(`/agent-assets/${assetId}/rule-json`)
}
export function saveAgentAssetRuleJson(assetId, payload, options = {}) {
return apiRequest(`/agent-assets/${assetId}/rule-json`, {
method: 'PUT',
body: JSON.stringify(payload),
headers: buildWriteHeaders(options)
})
}
export function compareAgentAssetSpreadsheetVersions(assetId, baseVersion, targetVersion) {
const query = new URLSearchParams({
base_version: String(baseVersion || '').trim(),

View File

@@ -165,7 +165,7 @@
<div v-if="selectedSpreadsheetChangeRecords.length" class="version-center-list">
<button
v-for="item in selectedSpreadsheetChangeRecords"
:key="`spreadsheet-change-${item.changed_at}-${item.actor}`"
:key="`spreadsheet-change-${item.id || item.changed_at}-${item.actor}`"
type="button"
class="version-center-item change-record-item"
@click="openSpreadsheetChangeDetail(item)"
@@ -175,9 +175,14 @@
<strong>{{ item.actor }}</strong>
<span>{{ item.time }}</span>
</div>
<b>{{ item.changed_cell_count }} 处改动</b>
<b>{{ item.changeCountLabel }}</b>
</div>
<p>{{ item.summary }}</p>
<small v-if="item.version">关联版本{{ item.version }}</small>
<small v-if="item.sheetPreview.length">
涉及工作表{{ item.sheetPreview.join('') }}
<template v-if="item.remainingSheetCount"> {{ item.changedSheetNames.length }} </template>
</small>
<div v-if="item.previewChanges.length" class="change-record-preview">
<span
v-for="change in item.previewChanges"
@@ -1124,6 +1129,10 @@
<span>修改时间</span>
<strong>{{ selectedSpreadsheetChangeRecord.time }}</strong>
</article>
<article v-if="selectedSpreadsheetChangeRecord.version">
<span>关联版本</span>
<strong>{{ selectedSpreadsheetChangeRecord.version }}</strong>
</article>
<article>
<span>修改工作表</span>
<strong>{{ selectedSpreadsheetChangeRecord.changed_sheet_count }}</strong>
@@ -1154,7 +1163,7 @@
{{ item.sheet_name }} · {{ item.meta.label }}
</span>
</div>
<p v-else>本次没有新增或删除工作表</p>
<p v-else>本次没有工作表级变化</p>
</section>
<section class="compare-panel compare-cell-panel">

View File

@@ -1695,12 +1695,30 @@ export default {
}
return (spreadsheetChangeRecordsByAsset.value[selectedSkill.value.id] || [])
.filter((item) => item?.changed_at)
.map((item) => ({
...item,
time: formatDateTime(item.changed_at),
previewChanges: Array.isArray(item.cell_changes) ? item.cell_changes.slice(0, 3) : [],
remainingChangeCount: Math.max((item.changed_cell_count || 0) - 3, 0)
}))
.map((item) => {
const sheetNames = [
...(Array.isArray(item.sheet_changes)
? item.sheet_changes.map((change) => normalizeText(change.sheet_name))
: []),
...(Array.isArray(item.cell_changes)
? item.cell_changes.map((change) => normalizeText(change.sheet_name))
: [])
].filter(Boolean)
const changedSheetNames = [...new Set(sheetNames)]
const previewChanges = Array.isArray(item.cell_changes) ? item.cell_changes.slice(0, 3) : []
return {
...item,
time: formatDateTime(item.changed_at),
changeCountLabel: item.changed_cell_count
? `${item.changed_cell_count} 处改动`
: `${item.changed_sheet_count || changedSheetNames.length || 0} 个工作表`,
changedSheetNames,
sheetPreview: changedSheetNames.slice(0, 4),
remainingSheetCount: Math.max(changedSheetNames.length - 4, 0),
previewChanges,
remainingChangeCount: Math.max((item.changed_cell_count || 0) - previewChanges.length, 0)
}
})
})
const selectedSpreadsheetChangeSheetRows = computed(() =>
Array.isArray(selectedSpreadsheetChangeRecord.value?.sheet_changes)
@@ -2133,13 +2151,24 @@ export default {
)
.join('|')
: ''
const sheetSignature = Array.isArray(latest.sheet_changes)
? latest.sheet_changes
.map((item) =>
[item?.sheet_name, item?.change_type]
.map((value) => normalizeText(value))
.join(':')
)
.join('|')
: ''
return [
latest.id,
latest.changed_at,
latest.actor,
latest.version,
latest.summary,
latest.changed_sheet_count,
latest.changed_cell_count,
sheetSignature,
previewSignature
]
.map((value) => normalizeText(value))
@@ -2538,7 +2567,21 @@ export default {
loadSpreadsheetChangeRecords(assetId).catch(() => {})
}
if (selectedSkill.value.usesJsonRiskRule) {
await loadRiskRuleJson(assetId)
try {
await loadRiskRuleJson(assetId)
} catch (jsonError) {
console.warn('Failed to load risk rule JSON:', jsonError)
const jsonMessage =
jsonError?.message || '风险规则 JSON 文件缺失或无法读取,请同步规则库后重试。'
toast(jsonMessage)
selectedSkill.value = {
...selectedSkill.value,
riskRuleJsonText: '{}',
riskRuleDescription:
selectedSkill.value.riskRuleDescription ||
'规则 JSON 尚未就绪,请联系管理员执行平台风险规则同步。'
}
}
}
}
} catch (error) {