后端新增风险图谱算法模块、风险观察与反馈服务、规则 DSL 校验器和可解释性引擎,完善系统仪表盘和财务仪表盘统计, 优化 agent 运行和编排执行链路,清理旧开发文档,前端新增 系统趋势、负载热力图等多种仪表盘图表组件,完善操作反馈 对话框和工作台日期选择器,优化报销创建和审批详情交互, 补充单元测试覆盖。
162 lines
4.9 KiB
JavaScript
162 lines
4.9 KiB
JavaScript
import { ref } from 'vue'
|
|
|
|
import { createOperationFeedback } from '../services/operationFeedback.js'
|
|
|
|
const LOW_RATING_MAX = 3
|
|
|
|
function pickText(...values) {
|
|
for (const value of values) {
|
|
const normalized = String(value || '').trim()
|
|
if (normalized) {
|
|
return normalized
|
|
}
|
|
}
|
|
return ''
|
|
}
|
|
|
|
function resolveUserId(currentUser = {}) {
|
|
return pickText(
|
|
currentUser.username,
|
|
currentUser.email,
|
|
currentUser.name,
|
|
'anonymous'
|
|
)
|
|
}
|
|
|
|
export function normalizeOperationFeedbackContext(context = {}, currentUser = {}) {
|
|
const traceSummary = context.trace_summary || context.traceSummary || null
|
|
const result = context.result && typeof context.result === 'object' ? context.result : {}
|
|
const resultSummary = pickText(
|
|
context.result_summary,
|
|
context.resultSummary,
|
|
result.answer,
|
|
result.message
|
|
)
|
|
|
|
return {
|
|
runId: pickText(context.run_id, context.runId),
|
|
conversationId: pickText(context.conversation_id, context.conversationId),
|
|
userId: pickText(context.user_id, context.userId, resolveUserId(currentUser)),
|
|
agent: pickText(context.selected_agent, context.selectedAgent, context.agent),
|
|
source: pickText(context.source, 'user_message'),
|
|
sessionType: pickText(context.session_type, context.sessionType),
|
|
operationType: pickText(context.operation_type, context.operationType, 'assistant_round'),
|
|
operationStatus: pickText(context.operation_status, context.operationStatus, context.status),
|
|
routeReason: pickText(context.route_reason, context.routeReason),
|
|
entrySource: pickText(context.entry_source, context.entrySource),
|
|
traceSummary,
|
|
resultSummary: resultSummary.slice(0, 500)
|
|
}
|
|
}
|
|
|
|
export function buildOperationFeedbackPayload(context = {}, feedback = {}, currentUser = {}) {
|
|
const normalizedContext = normalizeOperationFeedbackContext(context, currentUser)
|
|
const rating = Number(feedback.rating || 0)
|
|
const reason = String(feedback.reason || '').trim()
|
|
|
|
return {
|
|
run_id: normalizedContext.runId || null,
|
|
conversation_id: normalizedContext.conversationId || null,
|
|
user_id: normalizedContext.userId || null,
|
|
agent: normalizedContext.agent || null,
|
|
source: normalizedContext.source || null,
|
|
session_type: normalizedContext.sessionType || null,
|
|
operation_type: normalizedContext.operationType || 'assistant_round',
|
|
operation_status: normalizedContext.operationStatus || null,
|
|
rating,
|
|
reason: reason || null,
|
|
context_json: {
|
|
entry_source: normalizedContext.entrySource,
|
|
route_reason: normalizedContext.routeReason,
|
|
trace_summary: normalizedContext.traceSummary,
|
|
result_summary: normalizedContext.resultSummary,
|
|
low_rating: rating > 0 && rating <= LOW_RATING_MAX
|
|
}
|
|
}
|
|
}
|
|
|
|
export function useOperationFeedback({ currentUser, toast } = {}) {
|
|
const operationFeedbackDialog = ref({
|
|
open: false,
|
|
submitting: false,
|
|
error: '',
|
|
context: null
|
|
})
|
|
const promptedRunIds = new Set()
|
|
|
|
function openOperationFeedback(context = {}) {
|
|
const normalized = normalizeOperationFeedbackContext(context, currentUser?.value || {})
|
|
if (!normalized.runId || promptedRunIds.has(normalized.runId)) {
|
|
return
|
|
}
|
|
if (normalized.operationStatus && normalized.operationStatus !== 'succeeded') {
|
|
return
|
|
}
|
|
|
|
promptedRunIds.add(normalized.runId)
|
|
globalThis.setTimeout(() => {
|
|
operationFeedbackDialog.value = {
|
|
open: true,
|
|
submitting: false,
|
|
error: '',
|
|
context: normalized
|
|
}
|
|
}, 320)
|
|
}
|
|
|
|
function closeOperationFeedback() {
|
|
if (operationFeedbackDialog.value.submitting) {
|
|
return
|
|
}
|
|
operationFeedbackDialog.value = {
|
|
...operationFeedbackDialog.value,
|
|
open: false,
|
|
error: ''
|
|
}
|
|
}
|
|
|
|
async function submitOperationFeedbackRating(feedback = {}) {
|
|
const rating = Number(feedback.rating || 0)
|
|
if (!Number.isInteger(rating) || rating < 1 || rating > 5) {
|
|
operationFeedbackDialog.value = {
|
|
...operationFeedbackDialog.value,
|
|
error: '请选择 1 到 5 星评分。'
|
|
}
|
|
return
|
|
}
|
|
|
|
const context = operationFeedbackDialog.value.context || {}
|
|
operationFeedbackDialog.value = {
|
|
...operationFeedbackDialog.value,
|
|
submitting: true,
|
|
error: ''
|
|
}
|
|
|
|
try {
|
|
await createOperationFeedback(
|
|
buildOperationFeedbackPayload(context, feedback, currentUser?.value || {})
|
|
)
|
|
operationFeedbackDialog.value = {
|
|
open: false,
|
|
submitting: false,
|
|
error: '',
|
|
context: null
|
|
}
|
|
toast?.('评价已记录,后续会纳入智能体质量统计。')
|
|
} catch (error) {
|
|
operationFeedbackDialog.value = {
|
|
...operationFeedbackDialog.value,
|
|
submitting: false,
|
|
error: error?.message || '评价提交失败,请稍后重试。'
|
|
}
|
|
}
|
|
}
|
|
|
|
return {
|
|
operationFeedbackDialog,
|
|
openOperationFeedback,
|
|
closeOperationFeedback,
|
|
submitOperationFeedbackRating
|
|
}
|
|
}
|