feat: 新增预算中心本体与风险规则评分回填
后端新增预算本体解析模块和风险规则评分回填服务,优化规则 生成本体对齐和提示词构建,增强费用类型关键词和本体验证, 完善报销查询和审计接口,前端预算中心页面增加对话框和本 体工具函数,重构审计页面元数据和视图模型,补充单元测试。
This commit is contained in:
@@ -31,7 +31,7 @@ import {
|
||||
updateAgentAsset
|
||||
} from '../../services/agentAssets.js'
|
||||
import { loadOnlyOfficeApi } from '../../services/onlyoffice.js'
|
||||
import { isFinanceUser, isManagerUser } from '../../utils/accessControl.js'
|
||||
import { isFinanceUser, isManagerUser, isPlatformAdminUser } from '../../utils/accessControl.js'
|
||||
import { buildOnlyOfficeEditorConfig } from './onlyOfficePreviewConfig.js'
|
||||
import {
|
||||
buildReviewNote,
|
||||
@@ -69,6 +69,7 @@ import {
|
||||
} from './auditViewModel.js'
|
||||
import {
|
||||
createDefaultRiskRuleForm,
|
||||
RISK_RULE_BUSINESS_STAGE_OPTIONS,
|
||||
RISK_RULE_EXPENSE_CATEGORY_OPTIONS
|
||||
} from './auditViewRiskRuleModel.js'
|
||||
|
||||
@@ -144,11 +145,11 @@ export default {
|
||||
financialRules: [],
|
||||
riskRules: [],
|
||||
skills: [],
|
||||
mcp: [],
|
||||
tasks: []
|
||||
mcp: []
|
||||
})
|
||||
|
||||
const isAdmin = computed(() => isManagerUser(currentUser.value))
|
||||
const isAdmin = computed(() => isPlatformAdminUser(currentUser.value))
|
||||
const isRuleManager = computed(() => isManagerUser(currentUser.value))
|
||||
const isFinance = computed(() => isFinanceUser(currentUser.value))
|
||||
const activeMeta = computed(() => TAB_META[activeType.value])
|
||||
const activeTabLabel = computed(() => activeMeta.value.label)
|
||||
@@ -162,7 +163,7 @@ export default {
|
||||
const showVersionColumn = computed(() => activeMeta.value.showVersionColumn !== false)
|
||||
const showStatusColumn = computed(() => activeMeta.value.showStatusColumn !== false)
|
||||
const showOnlineColumn = computed(() => false)
|
||||
const showEnabledColumn = computed(() => activeType.value === 'riskRules')
|
||||
const showEnabledColumn = computed(() => false)
|
||||
const selectedSkillIsRule = computed(() => selectedSkill.value?.type === 'rules')
|
||||
const selectedSkillUsesSpreadsheet = computed(
|
||||
() => selectedSkillIsRule.value && Boolean(selectedSkill.value?.usesSpreadsheetRule)
|
||||
@@ -171,6 +172,9 @@ export default {
|
||||
() => selectedSkillIsRule.value && Boolean(selectedSkill.value?.usesJsonRiskRule)
|
||||
)
|
||||
const canManageSelected = computed(
|
||||
() => isRuleManager.value && Boolean(selectedSkill.value) && !selectedSkill.value?.isPreviewMock
|
||||
)
|
||||
const canAdminOperateSelected = computed(
|
||||
() => isAdmin.value && Boolean(selectedSkill.value) && !selectedSkill.value?.isPreviewMock
|
||||
)
|
||||
const canEditSelected = computed(
|
||||
@@ -180,7 +184,7 @@ export default {
|
||||
(isAdmin.value || isFinance.value)
|
||||
)
|
||||
const canCreateRiskRule = computed(
|
||||
() => activeType.value === 'riskRules' && (isAdmin.value || isFinance.value) && !detailBusy.value
|
||||
() => activeType.value === 'riskRules' && isRuleManager.value && !detailBusy.value
|
||||
)
|
||||
const latestRiskRuleTestSummary = computed(() => selectedSkill.value?.latestTestSummary || null)
|
||||
const riskRuleTestPassed = computed(() => Boolean(latestRiskRuleTestSummary.value?.test_passed))
|
||||
@@ -196,27 +200,20 @@ export default {
|
||||
const canOpenRiskRuleTest = computed(
|
||||
() =>
|
||||
selectedSkillUsesJsonRisk.value &&
|
||||
canEditSelected.value &&
|
||||
canAdminOperateSelected.value &&
|
||||
Boolean(selectedSkill.value?.id) &&
|
||||
!riskRuleGenerationBusy.value &&
|
||||
!riskRuleGenerationFailed.value &&
|
||||
!detailBusy.value
|
||||
!riskRuleGenerationFailed.value
|
||||
)
|
||||
const canDeleteRiskRule = computed(
|
||||
() =>
|
||||
selectedSkillUsesJsonRisk.value &&
|
||||
canEditSelected.value &&
|
||||
canAdminOperateSelected.value &&
|
||||
Boolean(selectedSkill.value?.id) &&
|
||||
!normalizeText(selectedSkill.value?.publishedVersion).replace('-', '') &&
|
||||
!detailBusy.value
|
||||
!normalizeText(selectedSkill.value?.publishedVersion).replace('-', '')
|
||||
)
|
||||
const canOpenRiskRuleReviewSubmit = computed(
|
||||
() =>
|
||||
selectedSkillUsesJsonRisk.value &&
|
||||
canSubmitReview.value &&
|
||||
!riskRuleInReview.value &&
|
||||
!riskRuleGenerationBusy.value &&
|
||||
!riskRuleGenerationFailed.value
|
||||
() => false
|
||||
)
|
||||
const canSubmitRiskRuleReview = computed(
|
||||
() =>
|
||||
@@ -224,17 +221,14 @@ export default {
|
||||
riskRuleTestPassed.value
|
||||
)
|
||||
const canReturnRiskRule = computed(
|
||||
() => selectedSkillUsesJsonRisk.value && canManageSelected.value && riskRuleInReview.value
|
||||
() => false
|
||||
)
|
||||
const canPublishRiskRule = computed(
|
||||
() =>
|
||||
selectedSkillUsesJsonRisk.value &&
|
||||
canManageSelected.value &&
|
||||
riskRuleInReview.value &&
|
||||
riskRuleTestPassed.value
|
||||
false
|
||||
)
|
||||
const canToggleRiskRuleEnabled = computed(
|
||||
() => selectedSkillUsesJsonRisk.value && canManageSelected.value && !detailBusy.value
|
||||
() => selectedSkillUsesJsonRisk.value && canManageSelected.value
|
||||
)
|
||||
const riskRuleCreateBusy = computed(() => actionState.value === 'generate-risk-rule')
|
||||
const canEditMarkdown = computed(() => canEditSelected.value && selectedSkillIsRule.value)
|
||||
@@ -242,7 +236,11 @@ export default {
|
||||
() => selectedSkill.value?.displayVersion === selectedSkill.value?.workingVersion
|
||||
)
|
||||
const canSubmitReview = computed(
|
||||
() => canEditSelected.value && selectedSkillIsRule.value && isDisplayingWorkingVersion.value
|
||||
() =>
|
||||
!selectedSkillUsesJsonRisk.value &&
|
||||
canEditSelected.value &&
|
||||
selectedSkillIsRule.value &&
|
||||
isDisplayingWorkingVersion.value
|
||||
)
|
||||
const hasReviewSubmitReviewers = computed(() => reviewSubmitReviewerOptions.value.length > 0)
|
||||
const canReviewSelected = computed(
|
||||
@@ -370,7 +368,7 @@ export default {
|
||||
)
|
||||
const showStatusFilter = computed(() => true)
|
||||
const showOnlineFilter = computed(() => false)
|
||||
const showEnabledFilter = computed(() => activeType.value === 'riskRules')
|
||||
const showEnabledFilter = computed(() => false)
|
||||
const selectedRiskScenarioLabel = computed(
|
||||
() =>
|
||||
RISK_SCENARIO_OPTIONS.find((item) => item.value === selectedRiskScenario.value)?.label ||
|
||||
@@ -646,6 +644,7 @@ export default {
|
||||
const detail = await generateRiskRuleAsset(
|
||||
{
|
||||
business_domain: 'expense',
|
||||
business_stage: riskRuleCreateForm.value.business_stage,
|
||||
expense_category: riskRuleCreateForm.value.expense_category,
|
||||
rule_title: ruleTitle,
|
||||
requires_attachment: Boolean(riskRuleCreateForm.value.requires_attachment),
|
||||
@@ -1007,8 +1006,13 @@ export default {
|
||||
}
|
||||
|
||||
async function loadAssets(options = {}) {
|
||||
loading.value = true
|
||||
errorMessage.value = ''
|
||||
const shouldShowLoading = !options.silent && !options.background
|
||||
if (shouldShowLoading) {
|
||||
loading.value = true
|
||||
}
|
||||
if (!options.silent) {
|
||||
errorMessage.value = ''
|
||||
}
|
||||
|
||||
try {
|
||||
const payload = await fetchAgentAssets({ assetType: activeMeta.value.assetType })
|
||||
@@ -1037,6 +1041,9 @@ export default {
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
if (options.silent || options.background) {
|
||||
return
|
||||
}
|
||||
if (activeMeta.value.assetType === 'rule') {
|
||||
assetBuckets.value = {
|
||||
...assetBuckets.value,
|
||||
@@ -1056,12 +1063,14 @@ export default {
|
||||
toast(errorMessage.value)
|
||||
}
|
||||
} finally {
|
||||
loading.value = false
|
||||
if (shouldShowLoading) {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshCurrentAssets() {
|
||||
await loadAssets({ force: true, silent: true })
|
||||
await loadAssets({ force: true, silent: true, background: true })
|
||||
}
|
||||
|
||||
async function loadSelectedAssetDetail(assetId) {
|
||||
@@ -1110,6 +1119,39 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
function mergeSelectedRuleLifecycle(detail) {
|
||||
if (!selectedSkill.value || !detail) {
|
||||
return
|
||||
}
|
||||
const next = buildDetailViewModel(detail, runs.value)
|
||||
selectedSkill.value = {
|
||||
...selectedSkill.value,
|
||||
status: next.status,
|
||||
statusValue: next.statusValue,
|
||||
statusTone: next.statusTone,
|
||||
publishedVersion: next.publishedVersion,
|
||||
workingVersion: next.workingVersion,
|
||||
currentVersion: next.currentVersion,
|
||||
displayVersion: next.displayVersion,
|
||||
reviewer: next.reviewer,
|
||||
publisher: next.publisher,
|
||||
publishedAt: next.publishedAt,
|
||||
isOnlineValue: next.isOnlineValue,
|
||||
isOnlineLabel: next.isOnlineLabel,
|
||||
isOnlineTone: next.isOnlineTone,
|
||||
isEnabledValue: next.isEnabledValue,
|
||||
isEnabledLabel: next.isEnabledLabel,
|
||||
isEnabledTone: next.isEnabledTone,
|
||||
latestTestSummary: next.latestTestSummary,
|
||||
lastOperationLabel: next.lastOperationLabel,
|
||||
lastOperationTone: next.lastOperationTone,
|
||||
publishMeta: next.publishMeta,
|
||||
publishState: next.publishState,
|
||||
updatedAt: next.updatedAt,
|
||||
configJson: next.configJson
|
||||
}
|
||||
}
|
||||
|
||||
async function loadRiskRuleJson(assetId) {
|
||||
if (!assetId || !selectedSkill.value?.usesJsonRiskRule) {
|
||||
return
|
||||
@@ -1525,6 +1567,9 @@ export default {
|
||||
}
|
||||
|
||||
function openRiskRuleTestDialog() {
|
||||
if (detailBusy.value) {
|
||||
return
|
||||
}
|
||||
if (!canOpenRiskRuleTest.value) {
|
||||
if (!selectedSkill.value?.id) {
|
||||
toast('规则详情还没有加载完成,请稍后再测试。')
|
||||
@@ -1544,7 +1589,8 @@ export default {
|
||||
}
|
||||
await refreshCurrentAssets()
|
||||
if (selectedSkill.value?.id) {
|
||||
await loadSelectedAssetDetail(selectedSkill.value.id)
|
||||
const detail = await fetchAgentAssetDetail(selectedSkill.value.id)
|
||||
mergeSelectedRuleLifecycle(detail)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1659,15 +1705,15 @@ export default {
|
||||
return
|
||||
}
|
||||
const assetId = selectedSkill.value.id
|
||||
const nextEnabled = !selectedSkill.value.isEnabledValue
|
||||
const nextEnabled = !selectedSkill.value.isOnlineValue
|
||||
actionState.value = 'toggle-risk-rule-enabled'
|
||||
try {
|
||||
await setRiskRuleAssetEnabled(assetId, nextEnabled, { actor: resolveActor() })
|
||||
const detail = await setRiskRuleAssetEnabled(assetId, nextEnabled, { actor: resolveActor() })
|
||||
mergeSelectedRuleLifecycle(detail)
|
||||
await refreshCurrentAssets()
|
||||
await loadSelectedAssetDetail(assetId)
|
||||
toast(nextEnabled ? '风险规则已启用。' : '风险规则已停用,不会进入业务扫描。')
|
||||
toast(nextEnabled ? '风险规则已上线。' : '风险规则已下线,不会进入业务扫描。')
|
||||
} catch (error) {
|
||||
toast(error?.message || '风险规则启用状态更新失败,请稍后重试。')
|
||||
toast(error?.message || '风险规则上线状态更新失败,请稍后重试。')
|
||||
} finally {
|
||||
actionState.value = ''
|
||||
}
|
||||
@@ -1851,6 +1897,7 @@ export default {
|
||||
riskRuleReturnOpen,
|
||||
riskRulePublishOpen,
|
||||
riskRuleReturnNote,
|
||||
riskRuleBusinessStageOptions: RISK_RULE_BUSINESS_STAGE_OPTIONS,
|
||||
riskRuleExpenseCategoryOptions: RISK_RULE_EXPENSE_CATEGORY_OPTIONS,
|
||||
showReviewNote,
|
||||
spreadsheetUploadInput,
|
||||
|
||||
Reference in New Issue
Block a user