修复小bug,剩余语音未测试
This commit is contained in:
parent
1a9423951c
commit
1dc2883922
|
|
@ -505,23 +505,25 @@ public class StudyScoreController extends BaseController
|
|||
return "FALSE";
|
||||
}
|
||||
|
||||
// ✅ 修复:去除所有空格,确保格式一致
|
||||
String noSpaces = trimmed.replaceAll("\\s+", "");
|
||||
|
||||
// 处理选项前缀:去除"A. "、"B. "等前缀
|
||||
// 支持格式:
|
||||
// - "A. 选项内容" -> "A" 或 "选项内容"
|
||||
// - "A.选项内容" -> "A" 或 "选项内容"
|
||||
// - "A.选项内容" -> "A" 或 "选项内容"(已去除空格)
|
||||
// - "A" -> "A"
|
||||
String normalized = trimmed;
|
||||
String normalized = noSpaces;
|
||||
|
||||
// 如果以"字母. "或"字母."开头,提取字母部分
|
||||
if (trimmed.matches("^[A-F]\\.\\s*.*"))
|
||||
// 如果以"字母."开头,提取字母部分
|
||||
if (noSpaces.matches("^[A-F]\\..*"))
|
||||
{
|
||||
// 提取字母(如 "A. 选项内容" -> "A")
|
||||
normalized = trimmed.substring(0, 1).toUpperCase();
|
||||
// 提取字母(如 "A.选项内容" -> "A")
|
||||
normalized = noSpaces.substring(0, 1).toUpperCase();
|
||||
}
|
||||
else if (trimmed.length() == 1 && trimmed.matches("[A-Fa-f]"))
|
||||
else if (noSpaces.length() == 1 && noSpaces.matches("[A-Fa-f]"))
|
||||
{
|
||||
// 单个字母(如 "A" -> "A")
|
||||
normalized = trimmed.toUpperCase();
|
||||
normalized = noSpaces.toUpperCase();
|
||||
}
|
||||
|
||||
return normalized;
|
||||
|
|
@ -540,9 +542,13 @@ public class StudyScoreController extends BaseController
|
|||
*/
|
||||
private boolean compareMultipleAnswers(String studentAnswer, String correctAnswer, String optionsJson)
|
||||
{
|
||||
// ✅ 修复:去除所有空格,确保格式一致(与保存题目时的规范化逻辑一致)
|
||||
String normalizedStudentAnswer = studentAnswer.replaceAll("\\s+", "");
|
||||
String normalizedCorrectAnswer = correctAnswer.replaceAll("\\s+", "");
|
||||
|
||||
// 使用逗号分隔(不使用空格、分号等,避免选项内容中的这些字符导致问题)
|
||||
String[] studentParts = studentAnswer.split(",");
|
||||
String[] correctParts = correctAnswer.split(",");
|
||||
String[] studentParts = normalizedStudentAnswer.split(",");
|
||||
String[] correctParts = normalizedCorrectAnswer.split(",");
|
||||
java.util.Set<String> studentSet = new java.util.HashSet<>();
|
||||
java.util.Set<String> correctSet = new java.util.HashSet<>();
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,12 @@ public class StudyLearningRecord extends BaseEntity
|
|||
/** 课程名称(不持久化,仅用于显示) */
|
||||
private String courseName;
|
||||
|
||||
/** 学生姓名(不持久化,仅用于搜索和显示) */
|
||||
private String studentName;
|
||||
|
||||
/** 学生学号/信息编号(不持久化,仅用于搜索和显示) */
|
||||
private String studentNo;
|
||||
|
||||
/** 学习进度(百分比,0-100) */
|
||||
@Excel(name = "学习进度")
|
||||
private BigDecimal progress;
|
||||
|
|
@ -172,6 +178,26 @@ public class StudyLearningRecord extends BaseEntity
|
|||
this.courseName = courseName;
|
||||
}
|
||||
|
||||
public String getStudentName()
|
||||
{
|
||||
return studentName;
|
||||
}
|
||||
|
||||
public void setStudentName(String studentName)
|
||||
{
|
||||
this.studentName = studentName;
|
||||
}
|
||||
|
||||
public String getStudentNo()
|
||||
{
|
||||
return studentNo;
|
||||
}
|
||||
|
||||
public void setStudentNo(String studentNo)
|
||||
{
|
||||
this.studentNo = studentNo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ public class StudyLearningRecordServiceImpl implements IStudyLearningRecordServi
|
|||
@Override
|
||||
public StudyLearningRecord selectLearningRecordById(Long id)
|
||||
{
|
||||
// ✅ 直接返回数据库中的进度值,与APP端保持一致
|
||||
return learningRecordMapper.selectLearningRecordById(id);
|
||||
}
|
||||
|
||||
|
|
@ -65,15 +66,9 @@ public class StudyLearningRecordServiceImpl implements IStudyLearningRecordServi
|
|||
{
|
||||
List<StudyLearningRecord> records = learningRecordMapper.selectLearningRecordList(learningRecord);
|
||||
|
||||
// 实时计算每条记录的进度(与前端保持一致)
|
||||
for (StudyLearningRecord record : records)
|
||||
{
|
||||
if (record.getStudentId() != null && record.getCourseId() != null)
|
||||
{
|
||||
BigDecimal realTimeProgress = calculateCourseProgressForDisplay(record.getStudentId(), record.getCourseId());
|
||||
record.setProgress(realTimeProgress);
|
||||
}
|
||||
}
|
||||
// ✅ 直接返回数据库中的进度值,与APP端保持一致
|
||||
// APP端会在学习时计算进度并上报保存,后台直接显示保存的值即可
|
||||
logger.info("【管理端查询】查询到 {} 条学习记录,直接使用数据库中的进度值", records.size());
|
||||
|
||||
return records;
|
||||
}
|
||||
|
|
@ -84,6 +79,8 @@ public class StudyLearningRecordServiceImpl implements IStudyLearningRecordServi
|
|||
*/
|
||||
private BigDecimal calculateCourseProgressForDisplay(Long studentId, Long courseId)
|
||||
{
|
||||
logger.info("【进度计算】开始计算 - 学生ID={}, 课程ID={}", studentId, courseId);
|
||||
|
||||
// 查询课程包含的所有课件
|
||||
StudyCourseware query = new StudyCourseware();
|
||||
query.setCourseId(courseId);
|
||||
|
|
@ -91,10 +88,12 @@ public class StudyLearningRecordServiceImpl implements IStudyLearningRecordServi
|
|||
|
||||
if (allCoursewareList == null || allCoursewareList.isEmpty())
|
||||
{
|
||||
logger.warn("【进度计算】课程{}没有课件", courseId);
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
int totalCoursewareCount = allCoursewareList.size();
|
||||
logger.info("【进度计算】课程共有{}个课件", totalCoursewareCount);
|
||||
|
||||
// 查询该学员对该课程的所有学习详情记录
|
||||
StudyLearningDetail detailQuery = new StudyLearningDetail();
|
||||
|
|
@ -136,6 +135,9 @@ public class StudyLearningRecordServiceImpl implements IStudyLearningRecordServi
|
|||
{
|
||||
StudyLearningDetail detail = coursewareDetailMap.get(cw.getId());
|
||||
|
||||
logger.info("【课件检查】课件ID={}, 类型={}, 标题={}, 配置时长={}秒",
|
||||
cw.getId(), cw.getType(), cw.getTitle(), cw.getDuration());
|
||||
|
||||
if ("video".equals(cw.getType()))
|
||||
{
|
||||
// 视频:判断是否完成
|
||||
|
|
@ -145,6 +147,9 @@ public class StudyLearningRecordServiceImpl implements IStudyLearningRecordServi
|
|||
int maxPosition = maxPositionMap.getOrDefault(cw.getId(), videoPosition);
|
||||
int duration = (cw.getDuration() != null && cw.getDuration() > 0) ? cw.getDuration() : 0;
|
||||
|
||||
logger.info("【视频课件】课件ID={}, 播放位置={}秒, 最大位置={}秒, 配置时长={}秒",
|
||||
cw.getId(), videoPosition, maxPosition, duration);
|
||||
|
||||
// 推断真实时长
|
||||
int realDuration = duration > 0 ? duration : (maxPosition + 3);
|
||||
|
||||
|
|
@ -155,11 +160,19 @@ public class StudyLearningRecordServiceImpl implements IStudyLearningRecordServi
|
|||
if (videoPosition == maxPosition && videoPosition >= 3)
|
||||
{
|
||||
isCompleted = true;
|
||||
logger.info("【完成判断】课件ID={} - 特殊判断通过(播放到最大位置{}秒)", cw.getId(), videoPosition);
|
||||
}
|
||||
// 常规判断:观看进度 >= 90%
|
||||
else if (realDuration > 0 && videoPosition >= realDuration * 0.9)
|
||||
{
|
||||
isCompleted = true;
|
||||
logger.info("【完成判断】课件ID={} - 常规判断通过({}秒 >= {}秒的90%)",
|
||||
cw.getId(), videoPosition, realDuration);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.info("【完成判断】课件ID={} - 未完成({}秒 < {}秒的90%)",
|
||||
cw.getId(), videoPosition, realDuration);
|
||||
}
|
||||
|
||||
if (isCompleted)
|
||||
|
|
@ -167,6 +180,11 @@ public class StudyLearningRecordServiceImpl implements IStudyLearningRecordServi
|
|||
completedCount++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.info("【视频课件】课件ID={} - 无学习记录或videoPosition为null, detail={}",
|
||||
cw.getId(), detail);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -174,6 +192,11 @@ public class StudyLearningRecordServiceImpl implements IStudyLearningRecordServi
|
|||
if (detail != null)
|
||||
{
|
||||
completedCount++;
|
||||
logger.info("【非视频课件】课件ID={} - 已完成", cw.getId());
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.info("【非视频课件】课件ID={} - 无学习记录", cw.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -183,9 +206,15 @@ public class StudyLearningRecordServiceImpl implements IStudyLearningRecordServi
|
|||
{
|
||||
double completionRate = (double) completedCount / totalCoursewareCount * 100;
|
||||
BigDecimal progress = new BigDecimal(completionRate).setScale(2, BigDecimal.ROUND_HALF_UP);
|
||||
return progress.compareTo(new BigDecimal(100)) > 0 ? new BigDecimal(100) : progress;
|
||||
BigDecimal finalProgress = progress.compareTo(new BigDecimal(100)) > 0 ? new BigDecimal(100) : progress;
|
||||
|
||||
logger.info("【进度计算】完成 - 学生ID={}, 课程ID={}, 已完成{}/总共{} = {}%",
|
||||
studentId, courseId, completedCount, totalCoursewareCount, finalProgress);
|
||||
|
||||
return finalProgress;
|
||||
}
|
||||
|
||||
logger.warn("【进度计算】总课件数为0 - 学生ID={}, 课程ID={}", studentId, courseId);
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
|
|
@ -198,6 +227,7 @@ public class StudyLearningRecordServiceImpl implements IStudyLearningRecordServi
|
|||
@Override
|
||||
public List<StudyLearningRecord> selectLearningRecordListByStudentId(Long studentId)
|
||||
{
|
||||
// ✅ 直接返回数据库中的进度值,与APP端保持一致
|
||||
return learningRecordMapper.selectLearningRecordListByStudentId(studentId);
|
||||
}
|
||||
|
||||
|
|
@ -210,6 +240,7 @@ public class StudyLearningRecordServiceImpl implements IStudyLearningRecordServi
|
|||
@Override
|
||||
public List<StudyLearningRecord> selectLearningRecordListByCourseId(Long courseId)
|
||||
{
|
||||
// ✅ 直接返回数据库中的进度值,与APP端保持一致
|
||||
return learningRecordMapper.selectLearningRecordListByCourseId(courseId);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
<result property="studentId" column="student_id" />
|
||||
<result property="courseId" column="course_id" />
|
||||
<result property="courseName" column="course_name" />
|
||||
<result property="studentName" column="student_name" />
|
||||
<result property="studentNo" column="student_no" />
|
||||
<result property="progress" column="progress" />
|
||||
<result property="totalDuration" column="total_duration" />
|
||||
<result property="learnCount" column="learn_count" />
|
||||
|
|
@ -24,9 +26,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
</resultMap>
|
||||
|
||||
<sql id="selectLearningRecordVo">
|
||||
select lr.id, lr.student_id, lr.course_id, c.course_name, lr.progress, lr.total_duration, lr.learn_count, lr.video_progress, lr.video_total_duration, lr.last_learn_time, lr.last_video_position, lr.create_by, lr.create_time, lr.update_by, lr.update_time, lr.remark
|
||||
select lr.id, lr.student_id, lr.course_id, c.course_name,
|
||||
u.nick_name as student_name, u.user_id as student_no,
|
||||
lr.progress, lr.total_duration, lr.learn_count, lr.video_progress,
|
||||
lr.video_total_duration, lr.last_learn_time, lr.last_video_position,
|
||||
lr.create_by, lr.create_time, lr.update_by, lr.update_time, lr.remark
|
||||
from learning_record lr
|
||||
left join course c on lr.course_id = c.id
|
||||
left join sys_user u on lr.student_id = u.user_id
|
||||
</sql>
|
||||
|
||||
<select id="selectLearningRecordList" parameterType="StudyLearningRecord" resultMap="StudyLearningRecordResult">
|
||||
|
|
@ -38,6 +45,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
<if test="courseId != null">
|
||||
and lr.course_id = #{courseId}
|
||||
</if>
|
||||
<if test="studentName != null and studentName != ''">
|
||||
and u.nick_name like concat('%', #{studentName}, '%')
|
||||
</if>
|
||||
<if test="studentNo != null and studentNo != ''">
|
||||
and u.user_id = #{studentNo}
|
||||
</if>
|
||||
<!-- 数据范围过滤 -->
|
||||
${params.dataScope}
|
||||
</where>
|
||||
|
|
@ -46,24 +59,24 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
|
||||
<select id="selectLearningRecordById" parameterType="Long" resultMap="StudyLearningRecordResult">
|
||||
<include refid="selectLearningRecordVo"/>
|
||||
where id = #{id}
|
||||
where lr.id = #{id}
|
||||
</select>
|
||||
|
||||
<select id="selectLearningRecordByStudentAndCourse" resultMap="StudyLearningRecordResult">
|
||||
<include refid="selectLearningRecordVo"/>
|
||||
where student_id = #{studentId} and course_id = #{courseId} limit 1
|
||||
where lr.student_id = #{studentId} and lr.course_id = #{courseId} limit 1
|
||||
</select>
|
||||
|
||||
<select id="selectLearningRecordListByStudentId" parameterType="Long" resultMap="StudyLearningRecordResult">
|
||||
<include refid="selectLearningRecordVo"/>
|
||||
where student_id = #{studentId}
|
||||
order by last_learn_time desc
|
||||
where lr.student_id = #{studentId}
|
||||
order by lr.last_learn_time desc
|
||||
</select>
|
||||
|
||||
<select id="selectLearningRecordListByCourseId" parameterType="Long" resultMap="StudyLearningRecordResult">
|
||||
<include refid="selectLearningRecordVo"/>
|
||||
where course_id = #{courseId}
|
||||
order by last_learn_time desc
|
||||
where lr.course_id = #{courseId}
|
||||
order by lr.last_learn_time desc
|
||||
</select>
|
||||
|
||||
<insert id="insertLearningRecord" parameterType="StudyLearningRecord" useGeneratedKeys="true" keyProperty="id">
|
||||
|
|
|
|||
|
|
@ -1,12 +1,21 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
|
||||
<el-form-item label="课程ID" prop="courseId">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="100px">
|
||||
<el-form-item label="罪犯姓名" prop="studentName">
|
||||
<el-input
|
||||
v-model="queryParams.courseId"
|
||||
placeholder="请输入课程ID"
|
||||
v-model="queryParams.studentName"
|
||||
placeholder="请输入罪犯姓名"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
style="width: 200px"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="信息编号" prop="studentNo">
|
||||
<el-input
|
||||
v-model="queryParams.studentNo"
|
||||
placeholder="请输入信息编号"
|
||||
clearable
|
||||
style="width: 200px"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
|
@ -31,10 +40,12 @@
|
|||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="recordList">
|
||||
<el-table-column label="记录ID" align="center" prop="id" />
|
||||
<el-table-column label="学员ID" align="center" prop="studentId" />
|
||||
<el-table-column label="记录ID" align="center" prop="id" width="80" />
|
||||
<el-table-column label="学员ID" align="center" prop="studentId" width="100" />
|
||||
<el-table-column label="罪犯姓名" align="center" prop="studentName" width="120" />
|
||||
<el-table-column label="信息编号" align="center" prop="studentNo" width="120" />
|
||||
<el-table-column label="课程名称" align="center" prop="courseName" min-width="150" />
|
||||
<el-table-column label="学习次数" align="center" prop="learnCount" />
|
||||
<el-table-column label="学习次数" align="center" prop="learnCount" width="100" />
|
||||
<el-table-column label="累计时长" align="center" prop="totalDuration">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ formatDuration(scope.row.totalDuration) }}</span>
|
||||
|
|
@ -86,7 +97,8 @@ export default {
|
|||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
courseId: null
|
||||
studentName: null,
|
||||
studentNo: null
|
||||
},
|
||||
// 刷新进度中
|
||||
refreshing: false
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import config from '@/utils/config.js'
|
||||
|
||||
const APP_DEV_HOST = '192.168.137.1'
|
||||
const APP_DEV_HOST = '192.168.1.80'
|
||||
const APP_DEV_PORT = 30091
|
||||
|
||||
export default {
|
||||
|
|
@ -12,8 +12,8 @@
|
|||
|
||||
// App环境:配置服务器地址(生产环境使用生产服务器)
|
||||
// #ifdef APP-PLUS
|
||||
// 强制使用新的服务器地址 192.168.137.1
|
||||
const PROD_HOST = '192.168.137.1'
|
||||
// 强制使用新的服务器地址 192.168.1.80
|
||||
const PROD_HOST = '192.168.1.80'
|
||||
const PROD_PORT = 30091
|
||||
|
||||
// 无条件强制更新配置(清除旧配置)
|
||||
|
|
|
|||
|
|
@ -392,8 +392,12 @@ export default {
|
|||
answerArray.push(option)
|
||||
}
|
||||
|
||||
this.$set(this.answers, questionId, answerArray.join(','))
|
||||
// 修复:统一使用完整选项文本,并排序确保一致性
|
||||
const sortedAnswer = answerArray.sort().join(',')
|
||||
this.$set(this.answers, questionId, sortedAnswer)
|
||||
this.saveAnswers()
|
||||
|
||||
console.log(`多选题答案更新 - 题目${questionId}:`, sortedAnswer)
|
||||
},
|
||||
|
||||
isOptionSelected(questionId, option) {
|
||||
|
|
@ -498,9 +502,15 @@ export default {
|
|||
console.warn('题目数据不完整:', question)
|
||||
return null
|
||||
}
|
||||
const answer = this.answers[question.id] || ''
|
||||
console.log(`题目${question.id} [${question.questionType}]:`, {
|
||||
questionContent: question.questionContent,
|
||||
userAnswer: answer,
|
||||
correctAnswer: question.correctAnswer
|
||||
})
|
||||
return {
|
||||
questionId: question.id,
|
||||
answer: this.answers[question.id] || ''
|
||||
answer: answer
|
||||
}
|
||||
}).filter(item => item !== null) // 过滤掉无效项
|
||||
|
||||
|
|
|
|||
|
|
@ -264,10 +264,7 @@ export default {
|
|||
}
|
||||
// #ifdef APP-PLUS
|
||||
// 新方案:使用服务器端识别,无需UTS插件
|
||||
speechRecorder.init()
|
||||
this.statusText = '准备就绪'
|
||||
this.isReady = true
|
||||
console.log('[Speech] 使用服务器端语音识别')
|
||||
this.initSpeechService()
|
||||
// #endif
|
||||
// #ifndef APP-PLUS
|
||||
this.statusText = '语音识别仅支持APP端'
|
||||
|
|
|
|||
|
|
@ -70,14 +70,23 @@ class SpeechRecorder {
|
|||
stop() {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!this.isRecording) {
|
||||
reject(new Error('未在录音中'))
|
||||
// 如果已经停止了,但有临时文件,返回该文件
|
||||
if (this.tempFilePath) {
|
||||
resolve(this.tempFilePath)
|
||||
} else {
|
||||
reject(new Error('未在录音中'))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
this.recorderManager.onStop((res) => {
|
||||
// 注册一次性监听器
|
||||
const onStopHandler = (res) => {
|
||||
this.tempFilePath = res.tempFilePath
|
||||
this.isRecording = false
|
||||
resolve(res.tempFilePath)
|
||||
})
|
||||
}
|
||||
|
||||
this.recorderManager.onStop(onStopHandler)
|
||||
this.recorderManager.stop()
|
||||
})
|
||||
}
|
||||
|
|
@ -92,6 +101,8 @@ class SpeechRecorder {
|
|||
return new Promise((resolve, reject) => {
|
||||
// 获取服务器配置
|
||||
const config = require('./config.js').default
|
||||
// 开发环境可以用 localhost:5000 测试
|
||||
// const serverUrl = 'http://localhost:5000' // Windows本地测试
|
||||
const serverUrl = config.API_BASE_URL
|
||||
|
||||
uni.uploadFile({
|
||||
|
|
@ -157,6 +168,42 @@ class SpeechRecorder {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步评测方法(用于已停止录音的场景)
|
||||
* @param {string} referenceText 参考文本
|
||||
* @param {number} questionId 题目ID(可选)
|
||||
* @returns {Promise<Object>} 评测结果
|
||||
*/
|
||||
async evaluateAsync(referenceText, questionId = null) {
|
||||
try {
|
||||
console.log('[语音评测] 开始异步评测', { referenceText, questionId, tempFilePath: this.tempFilePath })
|
||||
|
||||
// 检查是否有录音文件
|
||||
if (!this.tempFilePath) {
|
||||
throw new Error('没有可用的录音文件')
|
||||
}
|
||||
|
||||
// 上传并识别
|
||||
const result = await this.uploadAndRecognize(this.tempFilePath, {
|
||||
referenceText: referenceText,
|
||||
questionId: questionId
|
||||
})
|
||||
|
||||
console.log('[语音评测] 评测成功', result)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
...result
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[语音评测] 评测失败', error)
|
||||
return {
|
||||
success: false,
|
||||
error: error.message || '评测失败'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 导出单例
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user