fix(agent-assets): save rule markdown atomically

This commit is contained in:
caoxiaozhu
2026-07-20 10:30:48 +08:00
parent 208e1bed59
commit aa6a7d1250
10 changed files with 516 additions and 18 deletions

View File

@@ -405,6 +405,14 @@ export function createAgentAssetVersion(assetId, payload, options = {}) {
})
}
export function saveAgentAssetRuleMarkdown(assetId, payload, options = {}) {
return apiRequest(`/agent-assets/${assetId}/rule-markdown`, {
method: 'POST',
body: JSON.stringify(payload),
headers: buildWriteHeaders(options)
})
}
export function createAgentAssetReview(assetId, payload, options = {}) {
return apiRequest(`/agent-assets/${assetId}/reviews`, {
method: 'POST',

View File

@@ -1,8 +1,7 @@
import {
activateAgentAsset,
createAgentAssetVersion,
restoreAgentAssetVersion,
updateAgentAsset
saveAgentAssetRuleMarkdown
} from '../../services/agentAssets.js'
import {
buildRuleConfigPayload,
@@ -26,16 +25,6 @@ export function useAuditRuleVersionActions({
resolveActor,
toast
}) {
async function persistRuleRuntimeConfig(asset, runtimeRule) {
await updateAgentAsset(
asset.id,
{
config_json: buildRuleConfigPayload(asset, runtimeRule)
},
{ actor: resolveActor() }
)
}
async function saveRuleVersion({ action, changeNote, successLabel }) {
if (
!selectedSkill.value ||
@@ -62,18 +51,18 @@ export function useAuditRuleVersionActions({
actionState.value = action
try {
await createAgentAssetVersion(
const actor = resolveActor()
await saveAgentAssetRuleMarkdown(
selectedSkill.value.id,
{
version: nextVersion,
content: buildMarkdownVersionContent(selectedSkill.value.markdownContent, runtimeRule),
content_type: 'markdown',
config_json: buildRuleConfigPayload(selectedSkill.value, runtimeRule),
change_note: changeNote,
created_by: resolveActor()
created_by: actor
},
{ actor: resolveActor() }
{ actor }
)
await persistRuleRuntimeConfig(selectedSkill.value, runtimeRule)
await refreshCurrentAssets()
await loadSelectedAssetDetail(selectedSkill.value.id, { silent: true })
toast(`${successLabel} ${nextVersion}`)

View File

@@ -0,0 +1,73 @@
import assert from 'node:assert/strict'
import test from 'node:test'
import { ref } from 'vue'
import { useAuditRuleVersionActions } from '../src/views/scripts/useAuditRuleVersionActions.js'
test('规则 Markdown 与运行时 JSON 通过单一原子接口保存', async (t) => {
const requests = []
const originalFetch = globalThis.fetch
t.after(() => {
globalThis.fetch = originalFetch
})
globalThis.fetch = async (url, options) => {
requests.push({ url, options })
return {
ok: true,
status: 201,
json: async () => ({ version: 'v1.0.1' })
}
}
let refreshCount = 0
let detailCount = 0
const notices = []
const actionState = ref('')
const actions = useAuditRuleVersionActions({
selectedSkill: ref({
id: 'rule-id',
currentVersion: 'v1.0.0',
markdownContent: '# 差旅规则',
runtimeRuleText: JSON.stringify({
kind: 'policy_rule_draft',
version: 2,
template_key: 'general_policy_v1',
rule_name: '差旅规则',
scenario: 'travel',
review_required: true
}),
config_json: { preserved: 'server-field' },
usesSpreadsheetRule: false
}),
selectedSkillIsRule: ref(true),
canEditMarkdown: ref(true),
canManageSelected: ref(true),
actionState,
detailBusy: ref(false),
refreshCurrentAssets: async () => {
refreshCount += 1
},
loadSelectedAssetDetail: async () => {
detailCount += 1
},
resolveActor: () => 'username:finance',
toast: (message) => notices.push(message)
})
await actions.saveRuleMarkdown()
assert.equal(requests.length, 1)
assert.equal(requests[0].url, '/api/v1/agent-assets/rule-id/rule-markdown')
assert.equal(requests[0].options.method, 'POST')
const body = JSON.parse(requests[0].options.body)
assert.equal(body.version, 'v1.0.1')
assert.equal(body.created_by, 'username:finance')
assert.equal(body.config_json.runtime_rule.version, 2)
assert.equal(body.config_json.preserved, 'server-field')
assert.match(body.content, /```expense-rule/)
assert.equal('content_type' in body, false)
assert.equal(refreshCount, 1)
assert.equal(detailCount, 1)
assert.equal(actionState.value, '')
assert.deepEqual(notices, ['规则 Markdown 已保存为 v1.0.1。'])
})