v4.2 冲刺版+每日推送+支付修复+全量代码评审
## 新增功能 - 冲刺版 ¥49.9/月:完整支付→激活→权益扣减链路 - 每日一题定时推送(@nestjs/schedule,早8点微信订阅消息) - miniprogram-ci 编译上传脚本(scripts/upload-mp.js) ## Bug修复 - 套餐值统一:vip→growth/sprint(interview轮次限制、analyze次数检查) - member/pay 移除开发绕过:改为订单校验后激活 - progress→report 参数名不匹配:id→interviewId - result.vue resume.create() 参数传错(对象→独立参数) - resume.vue analyze请求缺少Authorization header - bank.vue contribution请求缺少Authorization header - member.vue startPay() 缺少try/catch导致网络错误崩溃 - login.vue 调试面板 v-if="true" 生产泄漏 ## 配置 - 微信支付生产证书就位(商户号1113760598) - .env 清理冗余文件(删除.example/.production) - WX_NOTIFY_URL 更新为 zhiyinwx.yzrcloud.cn ## 文档 - PROJECT-STATUS.md v4.1→v4.2,状态全面更新 - DEPLOYMENT.md 新增小程序编译上传章节、清理检查清单
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* 面试回答中的语气词/填充词分析
|
||||
* 检测用户回答中的语气词密度、语速估算等
|
||||
*/
|
||||
|
||||
const CHINESE_FILLER_WORDS = [
|
||||
'嗯', '啊', '呃', '哦', '那个', '这个', '然后', '就是',
|
||||
'对吧', '所以说', '反正', '其实', '就是说', '那', '那么',
|
||||
'还有一个', '另外', '基本上', '大概', '可能', '应该',
|
||||
'的话', '的时候', '一种', '一个', '一种', '可以说',
|
||||
]
|
||||
|
||||
const ENGLISH_FILLER_WORDS = [
|
||||
'um', 'uh', 'er', 'ah', 'like', 'you know', 'actually',
|
||||
'basically', 'literally', 'honestly', 'sort of', 'kind of',
|
||||
'i mean', 'you see', 'well', 'so', 'anyway',
|
||||
]
|
||||
|
||||
function countOccurrences(text: string, words: string[]): { word: string; count: number }[] {
|
||||
const result: { word: string; count: number }[] = []
|
||||
const lowerText = text.toLowerCase()
|
||||
for (const word of words) {
|
||||
const escaped = word.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
||||
const regex = new RegExp(escaped, 'g')
|
||||
const matches = lowerText.match(regex)
|
||||
if (matches && matches.length > 0) {
|
||||
result.push({ word, count: matches.length })
|
||||
}
|
||||
}
|
||||
return result.sort((a, b) => b.count - a.count)
|
||||
}
|
||||
|
||||
export function analyzeSpeech(text: string) {
|
||||
const totalChars = text.length
|
||||
const totalWords = text.split(/\s+/).filter(Boolean).length
|
||||
|
||||
const chineseFillers = countOccurrences(text, CHINESE_FILLER_WORDS)
|
||||
const englishFillers = countOccurrences(text, ENGLISH_FILLER_WORDS)
|
||||
const allFillers = [...chineseFillers, ...englishFillers]
|
||||
const totalFillerCount = allFillers.reduce((s, f) => s + f.count, 0)
|
||||
|
||||
// 估算语速(中文字符/秒,假设正常说话速度 ~3-4 字/秒)
|
||||
const estimatedDurationSec = Math.max(totalChars / 3.5, 10)
|
||||
const speechRate = totalChars / estimatedDurationSec
|
||||
|
||||
// 语气词密度
|
||||
const fillerDensity = totalChars > 0 ? totalFillerCount / totalChars : 0
|
||||
|
||||
// 评分:语气词越少越好
|
||||
let fillerScore = 100
|
||||
if (fillerDensity > 0.15) fillerScore = 40
|
||||
else if (fillerDensity > 0.10) fillerScore = 60
|
||||
else if (fillerDensity > 0.05) fillerScore = 80
|
||||
|
||||
// 判断是否过长/过短
|
||||
let lengthFeedback = ''
|
||||
if (totalChars < 20) lengthFeedback = '回答过短,建议展开阐述'
|
||||
else if (totalChars > 500) lengthFeedback = '回答偏长,建议精简重点'
|
||||
|
||||
return {
|
||||
totalChars,
|
||||
totalWords,
|
||||
fillerCount: totalFillerCount,
|
||||
fillerWords: allFillers.slice(0, 5),
|
||||
fillerDensity: Math.round(fillerDensity * 1000) / 10,
|
||||
fillerScore,
|
||||
estimatedDurationSec: Math.round(estimatedDurationSec),
|
||||
speechRate: Math.round(speechRate * 10) / 10,
|
||||
lengthFeedback,
|
||||
topFiller: allFillers[0]?.word || null,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user