IESM项目实训六
前一篇文章大概实现了基本的前后端交互方式,舍弃使用语音录入成绩类型,改为使用弹窗选择,该文章是具体两种方式代码。
单独成绩录入
后端相关URL:
找到当前第一个未曾录入学生的索引,不能为上一次期望录入同学,否则一个同学多次录入失败会不断重复该学生。无论上一位同学是否正常录入都不会影响录入下一位同学,它会在下一位同学的下一位录入,即失败会间隔一位同学再次录入。如果返回索引为-1,表示所有学生成绩已录入,前端停止音频录制等相关周期方法。
//找到未录入的第一个学生,返回该学生索引
@ResponseBody
@RequestMapping(value = "/inputStuIndex")
public Result<Integer> inputStuIndex(@RequestParam(name = "lastIndex") int lastIndex, @RequestParam(name = "type") int type) {
int index = -2;
if (this.stuList != null) {
for (int i = 0; i < this.stuList.size(); i++) {
if (type == 2) {
if ((this.stuList.get(i).getExamScores() == null||this.stuList.get(i).getExamScores() == 0) && i != lastIndex) {
index = i;
break;
}
} else if (type == 1) {
if ((this.stuList.get(i).getTestScores() == null||this.stuList.get(i).getTestScores() == 0) && i != lastIndex) {
index = i;
break;
}
} else if (type == 3) {
if ((this.stuList.get(i).getUsualScores() == null||this.stuList.get(i).getUsualScores() == 0) && i != lastIndex) {
index = i;
break;
}
} else {
System.out.println(type);
}
}
} else {
index=-1;
System.out.println("这门课没有学生可以录入了");
}
System.out.println("下一个录入学生索引:" + index);
return Result.OK(index);
}
语音合成索引对应学生姓名,用以提示教师当前录入学生姓名和表示开始录制音频信号。
//合成学生姓名语音
@RequestMapping(value = "/stuNameVoice")
public Result<Boolean> stuNameVoice(@RequestParam(name = "index") int index) {
VoiceCompose voiceCompose = new VoiceCompose();
Boolean result = false;
String startVoice = this.stuList.get(index).getStuName();
if (!voiceCompose.getMP3ByText(startVoice)) {
System.out.println("转换失败");
} else {
result = true;
voiceCompose.playMP3();
}
return Result.OK(result);
}
识别音频文件,处理识别结果,判断是否为0-100。正确则按照索引更新学生对应类型成绩,错误则不更新学生成绩,学生成绩仍为空,间隔一位同学后,系统会再次播放该学生姓名。
//识别成绩录入数据库
@ResponseBody
@RequestMapping(value = "/voiceInputScores")
public Result<List<Scores>> voiceInputScores(@RequestParam(name = "type") int type,
@RequestParam(name = "index") int index,
@RequestParam(name = "voiceBlobFile") MultipartFile voiceBlobFile,
@RequestParam(name = "scoresType") String scoresType) {
Scores stu = this.stuList.get(index);
String scores = "";
Integer score = 0;
byte[] voiceData = new byte[1024];
//将前端的参数转换为byte数组,进行识别处理
try {
voiceData = voiceBlobFile.getBytes();
} catch (IOException e) {
e.printStackTrace();
}
VoiceRecognition voiceRecognition = new VoiceRecognition();
scores = voiceRecognition.recognizeNum(voiceData);
if (!scores.equals("")) {
System.out.println("语音识别初始结果" + VoiceRecognition.getResultText());
score = Integer.parseInt(scores);
System.out.println("语音识别转换结果" + score);
if(score>100||score<0){
System.out.println("成绩录入格式不正确,不在0-100内");
return Result.error("成绩录入格式不正确,不在0-100内");
}else {
if (type == 1) {
this.stuList.get(index).setExamScores(score);
scoresService.updateExamScores(score, stu.getCsId(), stu.getLesOrd(), stu.getStuId());
System.out.println("更新了" + stuList.get(index).getStuName() + ":" + stuList.get(index).getExamScores());
} else if (type == 2) {
this.stuList.get(index).setTestScores(score);
scoresService.updateTestScores(score, stu.getCsId(), stu.getLesOrd(), stu.getStuId());
System.out.println("更新了" + stuList.get(index).getStuName() + ":" + stuList.get(index).getTestScores());
} else if (type == 3) {
this.stuList.get(index).setUsualScores(score);
scoresService.updateUsualScores(score, stu.getCsId(), stu.getLesOrd(), stu.getStuId());
System.out.println("更新了" + stuList.get(index).getStuName() + ":" + stuList.get(index).getUsualScores());
}
updateScores(this.stuList.get(index),scoresType);
}
} else {
String remind = "语音识别失败,请您重说一次";
return Result.error(remind);
System.out.println(remind);
}
return Result.OK(this.stuList);
}
总成绩通过任何方式录入,后台会根据各种类型成绩和所占比例得出总成绩。考虑学生课程存在两种总成绩类型,一种为百分制,一种为评级制,所以总成绩类型为String。数据库默认为百分制,如需修改则在前端进行修改。
public void updateScores(Scores scores,String scoresType){
int usualScores=-1;
int testScores=-1;
int scoreNum=-1;
if(scores.getUsualScores()==null){
usualScores=0;
}else{
usualScores=scores.getUsualScores();
}
if(scores.getExamScores()==null){
scoreNum=0;
}else{
scoreNum=scores.getExamScores();
}
if(scores.getTestScores()==null){
testScores=0;
}else{
testScores=scores.getTestScores();
}
double score=this.temporaryScores.getTestPercent()*testScores*0.01
+this.temporaryScores.getUsualPercent()*usualScores*0.01
+(100-this.temporaryScores.getTestPercent()-this.temporaryScores.getUsualPercent())*scoreNum*0.01;
String num;
if(scoresType.equals("百分制")){
num=(int)myround(score)+"";
System.out.println("原成绩"+score+" 四舍五入后"+num);
scores.setScores(num);
}else {
num=scoresToLevel((int)myround(score))+"";
System.out.println("原成绩"+score+" 四舍五入后"+num);
scores.setScores(num);
}
scoresService.updateScores(num,scores.getCsId(),scores.getLesOrd(),scores.getStuId());
}
正常计算总成绩结果为double,需要四舍五入后,转换为String。
//四舍五入double
public static double myround(double num)
{
BigDecimal b=new BigDecimal(num);
num=b.setScale(2,BigDecimal.ROUND_HALF_UP).doubleValue();
return num;
}
//评级制,总成绩生成
public static String scoresToLevel(int num){
String temp="";
if(num>=90&&num<=100){
temp="优";
}else if(num>=80&&num<=90){
temp="良";
}else if(num>=70&&num<=80){
temp="中";
}else if(num>=60&&num<=70){
temp="及格";
}else if(num<60){
temp="不及格";
}
return temp;
}
前端代码:
url: {
stuNameVoice: "ScoresInput/scores/stuNameVoice",
inputStuIndex: "ScoresInput/scores/inputStuIndex",
voiceInputScores: "ScoresInput/scores/voiceInputScores",
}
//语音录入按钮只可点击一次,控制不能重复点击响应
voiceController: true,
startRecording: function () {
if(this.inputState==="已提交"){
alert("该课程成绩已提交不可进行修改");
}else {
if (this.voiceController === true) {
let output = false;
let startVoice = "现在开始语音录入成绩,请选择录入成绩类型";
getAction(this.url.startInputScoresVoice, {startVoice: startVoice}).then((res) => {
if (res.result===true) {
output = res.result;
let that = this;
if (output === true) {
console.log("开始提示语音播放成功");
that.voiceController = false;
//弹出选择输入成绩类型
that.dialogVisible = true;
//等待输入类型选择
that.waitType = setInterval(function () {
if (that.dialogVisible === false) {
console.log("要输入的类型是" + that.type);
clearInterval(that.waitType);
//初始化索引
let index = -3;
//获取学生列表
let voiceBlobFile;//录入成绩的音频文件
let lastIndex = index;//防止该同学录入失败无间隔重复该学生,所以保存上一个同学的索引,避免相邻两次录入学生相同
that.inputScores = setInterval(function () {
//获取索引
getAction(that.url.inputStuIndex, {lastIndex: lastIndex, type: that.type}).then((res) => {
//如果没有为空值的学生,就表示学生成绩已经全部录入
if (res.result === -1) {
//如果所有同学录入,停止该周期函数,弹出弹窗提醒,按钮控制为true
if (recorder !== null) {
recorder.stop();
}
that.voiceController = true;
clearInterval(that.inputScores);
alert("学生成绩已全部录入");
} else {
index = res.result;
console.log("录入学生成绩索引" + index)
if (index !== -2) {
getAction(that.url.stuNameVoice, {index: index}).then((res) => {
console.log("学生姓名播放");
if (res.result === true) {
console.log("学生姓名播放成功了");
} else {
console.log("学生姓名播放失败");
}
//开始音频录制
Recorder.get(function (rec) {
recorder = rec
recorder.start()
})
})
} else if (index === -2) {
if (recorder !== null) {
recorder.stop();
}
that.voiceController = true;
clearInterval(that.inputScores);
alert("该课程没有学生!");
}
//因为每隔5秒录入一名学生,所以开始的4.5秒后获取音频数据,确保为当前学生的成绩音频
that.getVoiceFile = setTimeout(function () {
voiceBlobFile = recorder.getBlob();
//录入学生成绩
if (index >= 0) {
that.recognitionScores(that.type, index, voiceBlobFile);
}
//防止相邻两次都是同一学生
lastIndex = index;
}, 4500)
}
})
}, 5000)
}
}, 10);
} else {
console.log("开始提示语音播放失败");
}
}
})
}
}
},
//向后端传递录制成绩的音频,仅识别成绩
recognitionScores: function (type, index, voiceBlobFile) {
let formData = new FormData();
console.log(voiceBlobFile)
formData.append("voiceBlobFile", voiceBlobFile);
formData.append("index", index);
formData.append("type", type);
formData.append("scoresType", this.scoresTypeValue);
axios.post(this.url.voiceInputScores, formData).then((res) => {
if(res.success){
//将数据实时显示出来,不需要通过数据库查询
this.superFieldList = res.result;
this.dataSource=this.superFieldList;
console.log("成绩音频数据传递成功");
}else{
console.log("成绩音频数据传递失败");
}
})
},
语音姓名成绩录入
后端代码如下:
//识别成绩录入数据库
@ResponseBody
@RequestMapping(value = "/voiceInputAllScores")
public Result<List<Scores>> voiceInputAllScores(@RequestParam(name = "type") int type,
@RequestParam(name = "voiceBlobFile") MultipartFile voiceBlobFile,
@RequestParam(name = "scoresType") String scoresType) {
String scores = "";
Integer score = 0;
byte[] voiceData = new byte[1024];
//将前端的参数转换为byte数组,进行识别处理
try {
voiceData = voiceBlobFile.getBytes();
} catch (IOException e) {
e.printStackTrace();
}
VoiceRecognition voiceRecognition = new VoiceRecognition();
boolean judgement = voiceRecognition.recognizeDataVoice(voiceData);
String result = VoiceRecognition.getResultText();
String stuName = VoiceRecognition.stuNameRecognize(result);
scores = VoiceRecognition.format(result);
if (judgement && !stuName.equals("") && !scores.equals("")) {
score = Integer.parseInt(scores);
if (score > 100 || score < 0) {
System.out.println("成绩录入格式不正确,不在0-100内");
return Result.error("成绩录入格式不正确,不在0-100内");
}else {
Scores stu = new Scores();
int index = -1;
//找是否有名字对应的同学
String[] nameArr = new String[this.stuList.size()];
for (int i = 0; i < this.stuList.size(); i++) {
nameArr[i] = this.stuList.get(i).getStuName();
}
System.out.println(nameArr);
String similarWord = Search.getSimilarWord(stuName, nameArr);
System.out.println("找到的最相似姓名:" + similarWord);
for (int i = 0; i < stuList.size(); i++) {
if (Objects.equals(similarWord, stuList.get(i).getStuName())) {
stu = stuList.get(i);
index = i;
break;
}
}
//找不到就返回false,找到就更新数据库
if (index == -1) {
String remind = "没有找到该同学";
System.out.println(remind);
return Result.error(remind);
} else {
if (type == 1) {
this.stuList.get(index).setExamScores(score);
scoresService.updateExamScores(score, stu.getCsId(), stu.getLesOrd(), stu.getStuId());
System.out.println("更新了" + stuList.get(index).getExamScores());
} else if (type == 2) {
this.stuList.get(index).setTestScores(score);
scoresService.updateTestScores(score, stu.getCsId(), stu.getLesOrd(), stu.getStuId());
System.out.println("更新了" + stuList.get(index).getTestScores());
} else if (type == 3) {
this.stuList.get(index).setUsualScores(score);
scoresService.updateUsualScores(score, stu.getCsId(), stu.getLesOrd(), stu.getStuId());
System.out.println("更新了" + stuList.get(index).getStuName() + ":" + stuList.get(index).getUsualScores());
}
updateScores(this.stuList.get(index), scoresType);
}
}
} else {
String remind = "语音识别失败,请您重说一次";
System.out.println(remind);
return Result.error(remind);
}
return Result.OK(this.stuList);
}
与单独录入方式不同,该方法没有可以让过程暂停的条件,所以额外添加了一个方法获取当前需要录入学生数目。
//计算学生数目,可以获取学生列表
@ResponseBody
@RequestMapping(value = "/ScoresStuNum")
public Result<Integer> ScoresStuNum(@RequestParam(name = "type") int type,
@RequestParam(name = "cs_id") String cs_id,
@RequestParam(name = "les_ord") String les_ord) {
int stuNum = 0;
this.stuList = scoresService.getStuList(cs_id, les_ord);
List<Scores> stu = new ArrayList<>();
System.out.println(stuList);
System.out.println("输入类型"+type);
double s = 0.0;
if (stuList != null) {
if (type == 2) {
for (int i = 0; i < stuList.size(); i++) {
if (stuList.get(i).getExamScores() == null||this.stuList.get(i).getExamScores() == 0) {
stu.add(stuList.get(i));
stuNum = stuNum + 1;
}
}
} else if (type == 1) {
for (int i = 0; i < stuList.size(); i++) {
if (stuList.get(i).getTestScores() == null||stuList.get(i).getTestScores() == 0) {
stu.add(stuList.get(i));
stuNum = stuNum + 1;
}
}
} else if (type == 3) {
for (int i = 0; i < stuList.size(); i++) {
if (stuList.get(i).getUsualScores() == null||this.stuList.get(i).getUsualScores() == 0) {
stu.add(stuList.get(i));
stuNum = stuNum + 1;
}
}
}
} else {
System.out.println("这门课没有学生");
}
System.out.println(stuNum);
return Result.OK(stuNum);
}
后续考虑使用该方法更倾向于是教师不需要再查找对应学生,所以不需要设置自动暂停,需要教师操作手动暂停。
前端代码如下:
voiceInputAllScores: "ScoresInput/scores/voiceInputAllScores",
ScoresStuNum: "ScoresInput/scores/ScoresStuNum",
//语音名字都由教师语音录入
stuNameRecording: function () {
if(this.inputState==="已提交"){
alert("该课程成绩已提交不可进行修改");
}else {
if (this.voiceControllerTwo === true) {
this.voiceControllerTwo = false;
let output = false;
let startVoice = "现在开始语音录入姓名和成绩,请选择录入成绩类型";
getAction(this.url.startInputScoresVoice, {startVoice: startVoice}).then((res) => {
if (res.success) {
output = res.result;
let that = this;
if (output === true) {
console.log("开始提示语音播放成功");
//弹出选择输入成绩类型
that.dialogVisible = true;
//等待输入类型选择
that.waitTypeTwo = setInterval(function () {
if (that.dialogVisible === false) {
console.log("要输入的类型是" + that.type);
clearInterval(that.waitTypeTwo);
let voiceBlobFile;//录入成绩的音频文件
let n = 1;
that.inputScoresTwo = setInterval(function () {
let startVoice;
//判断合成的语音,第一次为现在开始录入
if (n === 1) {
startVoice = "现在开始录入:";
} else {
startVoice = "下个同学";
}
getAction(that.url.startInputScoresVoice, {startVoice: startVoice}).then((res) => {
if (res.success) {
Recorder.get(function (rec) {
recorder = rec
recorder.start()
})
that.getVoiceFile = setTimeout(function () {
voiceBlobFile = recorder.getBlob();
that.allScoresVoiceInput(voiceBlobFile);
recorder.stop()
}, 5000)
n = 2;
console.log("下一次语音合成" + n);
} else {
console.log("下一次语音合成错误")
}
})
}, 7000)
}
})
}
}
})
}
}
},
//三种录入类型人数统计,包括成绩为空和0的同学,后期未使用
ScoresStuList: function (cs_id, les_ord) {
this.stuNum = -2;
getAction(this.url.ScoresStuNum, {type: this.type, cs_id: cs_id, les_ord: les_ord}).then((res) => {
this.stuNum = res.result;
console.log("获取一次学生列表");
})
},
//传递包含姓名和成绩的音频文件
allScoresVoiceInput: function (voiceBlobFile) {
let formData = new FormData();
console.log(voiceBlobFile)
//formData=Object.assign(na,voiceBlobFilea);
formData.append("type", this.type);
formData.append("voiceBlobFile", voiceBlobFile);
formData.append("scoresType", this.scoresTypeValue);
axios.post(this.url.voiceInputAllScores, formData).then((res) => {
if (res.result!=null) {
//实时显示到前端
this.superFieldList = res.result;
this.dataSource=this.superFieldList;
console.log("数据传输成功");
} else {
console.log("数据传输失败");
}
})
},
录入成绩类型选择弹窗
弹窗:
不同值代表不同的录入类型,type为全局变量
<j-modal :visible="dialogVisible" title="请选择要输入成绩类型">
<a-button @click="usualTypeDialog">平时成绩</a-button>
<a-button @click="examTypeDialog">实验成绩</a-button>
<a-button @click="testTypeDialog">考试成绩</a-button>
</j-modal>
响应事件,type值更改,语音录入时会将该参数传入后台:
//各类成绩占比
usualPercent:0,
testPercent:0,
//选择成绩类型,设置成绩类型,关闭弹窗
usualTypeDialog: function () {
this.type = 3;
this.dialogVisible = false;
console.log("平时成绩" + this.type);
},
testTypeDialog: function () {
this.type = 2;
this.dialogVisible = false;
console.log("实验成绩" + this.type);
},
examTypeDialog: function () {
this.type = 1;
this.dialogVisible = false;
console.log("考试成绩" + this.type);
},
停止录制按钮响应事件
将所有周期函数和延迟执行的函数暂停,将按钮恢复可以点击的状态。
stopRecording: function () {
recorder.stop();
clearInterval(this.inputScores);
clearInterval(this.waitType);
clearInterval(this.inputScoresTwo);
clearInterval(this.waitTypeTwo);
console.log('已暂停');
this.voiceController = true;
this.voiceControllerTwo = true;
},