提交 b39a21bd authored 作者: 王鹏飞's avatar 王鹏飞

feat: 更新评分逻辑,支持AI评分选项和评分详情展示

上级 5e72e11b
import { getScoreList, getScoreDetail, updateScore } from '../api' import { getScoreList, getScoreDetail, updateScore } from '../api'
import { useUserStore } from '@/stores/user' import { useUserStore } from '@/stores/user'
import { useChat } from '@ezijing/ai-vue' import { useChat } from '@ezijing/ai-vue'
// import { generatePrompt } from '../prompt'
// import { generatePrompt as generatePrompt2 } from '../prompt2'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { import {
importType, importType,
...@@ -28,9 +26,10 @@ interface ScoreItem { ...@@ -28,9 +26,10 @@ interface ScoreItem {
comment: string comment: string
} }
export function useScore() { export function useScore(options: { includeAIScore?: boolean } = {}) {
const userStore = useUserStore() const userStore = useUserStore()
const isLoading = ref(false) const isLoading = ref(false)
const includeAIScore = options.includeAIScore ?? true
const { generateText } = useChat({ provider: 'volcano' }) const { generateText } = useChat({ provider: 'volcano' })
const fetchScoreList = async () => { const fetchScoreList = async () => {
...@@ -92,74 +91,75 @@ export function useScore() { ...@@ -92,74 +91,75 @@ export function useScore() {
const liveData = detail.live_data const liveData = detail.live_data
const competitionRule = detail.competition_rule const competitionRule = detail.competition_rule
let prompt = ''
// if (competitionRule?.competition == 1) {
// if (competitionRule?.questions == 1) {
// prompt = generatePrompt(detail.live_data)
// } else {
// prompt = generatePrompt2(detail.live_data)
// }
// } else {
const rules = JSON.parse(competitionRule?.config || '{}')?.rules || [] const rules = JSON.parse(competitionRule?.config || '{}')?.rules || []
rules.forEach((item: any) => { const ruleDataMap: Record<number, { key: string; label: string; data: unknown; extra?: string }> = {
if (item.rule_mode != 2) return 1: {
key: 'commodity_type',
if (item.type == 1) { label: '商品品类信息',
prompt += `请根据选手提交的商品品类信息进行评分(满分${item.percent}分): data: liveData.commodity_types,
选手提交内容: },
${JSON.stringify(liveData.commodity_types)}` 2: {
} else if (item.type == 2) { key: 'commodity_attr',
prompt += `请根据选手提交的商品属性配置信息进行评分(满分${item.percent}分): label: '商品属性配置信息',
重要性字典:${JSON.stringify(importType)} data: liveData.commodity_attrs,
必要性字典:${JSON.stringify(requiredType)} extra: `重要性字典:${JSON.stringify(importType)}
必要性字典:${JSON.stringify(requiredType)}`,
选手提交内容: },
${JSON.stringify(liveData.commodity_attrs)}` 3: {
} else if (item.type == 3) { key: 'commodity',
prompt += `请根据选手提交的商品信息维护内容进行评分(满分${item.percent}分): label: '商品信息维护内容',
发货模式字典:${JSON.stringify(deliveryMode)} data: liveData.commodities,
extra: `发货模式字典:${JSON.stringify(deliveryMode)}
发货时效字典:${JSON.stringify(deliveryTime)} 发货时效字典:${JSON.stringify(deliveryTime)}
订单库存扣减规则字典:${JSON.stringify(orderStockCount)} 订单库存扣减规则字典:${JSON.stringify(orderStockCount)}
运费模板字典:${JSON.stringify(shippingTemplate)} 运费模板字典:${JSON.stringify(shippingTemplate)}
售后政策字典:${JSON.stringify(afterSalesPolicy)} 售后政策字典:${JSON.stringify(afterSalesPolicy)}`,
选手提交内容:
${JSON.stringify(liveData.commodities)}`
} else if (item.type == 4) {
prompt += `请根据选手提交的直播话术进行评分(满分${item.percent}分):
选手提交内容:
${JSON.stringify(liveData.speeches)}`
}
prompt += `评分标准:
${item.content}`
})
prompt += `EXAMPLE JSON OUTPUT:
{
"commodity_type": {
"score": 5,
"comment": "这是评语"
}, },
"commodity_attr": { 4: {
"score": 5, key: 'speech',
"comment": "这是评语" label: '直播话术',
data: liveData.speeches,
}, },
"commodity": { 5: {
"score": 5, key: 'practice_record1',
"comment": "这是评语" label: '直播演练',
data: liveData.practices?.[0]?.records?.[0]?.subtitle,
}, },
"speech": { 6: {
"score": 5, key: 'report',
"comment": "这是评语" label: '直播总结汇报',
data: liveData.reports,
}, },
}` }
// } const autoRules = rules.filter((item: any) => item.rule_mode == 2 && ruleDataMap[Number(item.type)])
const prompt = autoRules
.map((item: any) => {
const ruleData = ruleDataMap[Number(item.type)]
return `【${item.name || ruleData.label}】
请根据选手提交的${ruleData.label}进行评分(满分${item.percent}分)
${ruleData.extra ? `${ruleData.extra}\n` : ''}
选手提交内容:
${JSON.stringify(ruleData.data)}
评分标准:
${item.content || '请根据提交内容客观评分,并说明扣分原因。'}`
})
.join('\n\n')
const outputExample = Object.fromEntries(
autoRules.map((item: any) => [ruleDataMap[Number(item.type)].key, { score: 0, comment: '请填写评分依据' }]),
)
const fullPrompt = `${prompt}
只输出 JSON,不要输出其他内容。每个评分项都必须返回,score 不得低于 0 或高于该项满分。
JSON 输出格式:
${JSON.stringify(outputExample, null, 2)}`
const messages = [ const messages = [
{ {
role: 'system', role: 'system',
content: `你是一位专业的电商运营评分专家,负责对选手的商品信息维护操作进行客观、准确的评分。`, content: `你是一位专业的大赛评分专家,负责根据评分规则对选手提交的内容进行客观、准确的评分。`,
}, },
{ role: 'user', content: prompt }, { role: 'user', content: fullPrompt },
] ]
const result = await generateText({ const result = await generateText({
model: 'deepseek-v4-flash-260425', model: 'deepseek-v4-flash-260425',
...@@ -169,7 +169,20 @@ ${JSON.stringify(liveData.speeches)}` ...@@ -169,7 +169,20 @@ ${JSON.stringify(liveData.speeches)}`
if (!result) return if (!result) return
try { try {
const scoreDetails: Record<string, ScoreItem> = JSON.parse(result.content) const parsed = JSON.parse(result.content)
const scoreDetails: Record<string, ScoreItem> = Object.fromEntries(
autoRules.map((item: any) => {
const key = ruleDataMap[Number(item.type)].key
const score = Number(parsed[key]?.score)
return [
key,
{
score: Number.isFinite(score) ? Math.max(0, Math.min(item.percent, score)) : 0,
comment: String(parsed[key]?.comment || ''),
},
]
}),
)
const totalScore = Object.values(scoreDetails).reduce((acc, curr) => acc + (curr.score || 0), 0) const totalScore = Object.values(scoreDetails).reduce((acc, curr) => acc + (curr.score || 0), 0)
console.log(scoreDetails) console.log(scoreDetails)
const myScore: ScoreDetail = { const myScore: ScoreDetail = {
...@@ -181,9 +194,10 @@ ${JSON.stringify(liveData.speeches)}` ...@@ -181,9 +194,10 @@ ${JSON.stringify(liveData.speeches)}`
updated_at: new Date().toLocaleString(), updated_at: new Date().toLocaleString(),
} }
const scores = [...detail.scores.filter((item: any) => item.checker_id != userStore.user?.id), myScore] const scores = [...detail.scores.filter((item: any) => item.checker_id != userStore.user?.id), myScore]
// 总平均分,过滤掉AI评分 const scoresForTotal = includeAIScore ? scores : scores.filter((item) => !item.is_ai)
const validScores = scores.filter((item) => !item.is_ai) const totalAverageScore = scoresForTotal.length
const totalAverageScore = validScores.reduce((acc, curr) => acc + (curr.total_score || 0), 0) / validScores.length ? scoresForTotal.reduce((acc, curr) => acc + (curr.total_score || 0), 0) / scoresForTotal.length
: 0
await updateScore({ await updateScore({
id: detail.id, id: detail.id,
check_status: 2, check_status: 2,
......
...@@ -52,10 +52,7 @@ const listOptions = computed(() => { ...@@ -52,10 +52,7 @@ const listOptions = computed(() => {
try { try {
const scores = row.score_details ? JSON.parse(row.score_details) : [] const scores = row.score_details ? JSON.parse(row.score_details) : []
return Array.isArray(scores) return Array.isArray(scores)
? scores ? scores.map((item) => `${item.is_ai ? 'AI' : item.checker_name}(${item.total_score})`).join('<br/>')
.filter((item) => !item.is_ai)
.map((item) => `${item.checker_name}(${item.total_score})`)
.join('<br/>')
: '' : ''
} catch (error) { } catch (error) {
console.log('评分人解析失败', error) console.log('评分人解析失败', error)
......
<script setup> <script setup>
import { getScoreDetail, updateScore } from '../api' import { getScoreDetail, updateScore } from '../api'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { useChat } from '@ezijing/ai-vue'
import { generatePrompt } from '../prompt'
import { generatePrompt as generatePrompt2 } from '../prompt2'
import { cloneDeep } from 'lodash-es' import { cloneDeep } from 'lodash-es'
import { useUserStore } from '@/stores/user' import { useUserStore } from '@/stores/user'
import { useEventListener } from '@vueuse/core' import { useEventListener } from '@vueuse/core'
...@@ -16,8 +13,6 @@ const CompetitionOperations = defineAsyncComponent(() => import('../components/C ...@@ -16,8 +13,6 @@ const CompetitionOperations = defineAsyncComponent(() => import('../components/C
const route = useRoute() const route = useRoute()
const id = route.query.id const id = route.query.id
const { isLoading, generateText } = useChat({ provider: 'volcano' })
const detail = ref(null) const detail = ref(null)
const scoreDetails = reactive({}) const scoreDetails = reactive({})
...@@ -61,26 +56,6 @@ onMounted(() => { ...@@ -61,26 +56,6 @@ onMounted(() => {
fetchDetail() fetchDetail()
}) })
const handleAIScore = async () => {
console.log('AI一键评分')
const prompt =
detail.value.competition_rule?.questions == 1
? generatePrompt(detail.value.live_data)
: generatePrompt2(detail.value.live_data)
const result = await generateText({ prompt, response_format: { type: 'json_object' } })
try {
const parsed = JSON.parse(result.content)
Object.assign(scoreDetails, parsed)
commitScore(1, true).then(() => {
ElMessage.success('保存成功')
})
} catch (error) {
console.log(error)
ElMessage.error('评分失败')
return
}
}
const handleSave = () => { const handleSave = () => {
commitScore().then(() => { commitScore().then(() => {
ElMessage.success('保存成功') ElMessage.success('保存成功')
...@@ -100,9 +75,9 @@ const commitScore = (status = 1, isAI = false) => { ...@@ -100,9 +75,9 @@ const commitScore = (status = 1, isAI = false) => {
// 去掉当前评分人的评分,并添加当前评分人的评分 // 去掉当前评分人的评分,并添加当前评分人的评分
scores = [...scores.filter((item) => !(item.checker_id == userStore.user.id && !item.is_ai)), myScore] scores = [...scores.filter((item) => !(item.checker_id == userStore.user.id && !item.is_ai)), myScore]
// 总平均分,过滤掉AI评分 const totalAverageScore = scores.length
const validScores = scores.filter((item) => !item.is_ai) ? scores.reduce((acc, curr) => acc + (curr.total_score || 0), 0) / scores.length
const totalAverageScore = validScores.reduce((acc, curr) => acc + (curr.total_score || 0), 0) / validScores.length : 0
return updateScore({ return updateScore({
id, id,
...@@ -144,14 +119,6 @@ useEventListener(window, 'pagehide', () => { ...@@ -144,14 +119,6 @@ useEventListener(window, 'pagehide', () => {
<el-form-item label="状态">{{ detail.check_status_name }}</el-form-item> <el-form-item label="状态">{{ detail.check_status_name }}</el-form-item>
<el-form-item label="提交时间">{{ detail.commit_time }}</el-form-item> <el-form-item label="提交时间">{{ detail.commit_time }}</el-form-item>
<el-form-item> <el-form-item>
<el-button
type="primary"
:loading="isLoading"
@click="handleAIScore"
v-if="detail.competition_rule?.competition == 1"
v-show="false"
>AI一键评分</el-button
>
<el-button type="primary" @click="handlePublishScore">提交成绩</el-button> <el-button type="primary" @click="handlePublishScore">提交成绩</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
......
Markdown 格式
0% 或
您添加了 0 人 到此讨论。请谨慎行事。
请先完成此评论的编辑!
请 注册 或者 后发表评论