Complete Course Design Project
This commit is contained in:
parent
ee757f2c72
commit
c4e220ed62
7
.env
Normal file
7
.env
Normal file
@ -0,0 +1,7 @@
|
||||
# AI帮你面试 - 环境配置
|
||||
|
||||
# API密钥配置
|
||||
AI_API_KEY=sk-0944c102849e45d9ab3f24d5169de289
|
||||
|
||||
# Flask应用配置
|
||||
FLASK_DEBUG=True
|
||||
0
.gitignore
vendored
Normal file
0
.gitignore
vendored
Normal file
@ -1,5 +1,8 @@
|
||||
# AI帮你面试
|
||||
|
||||
姓名 学号 主要贡献 (具体分工)
|
||||
蔡朗 2411020227 核心逻辑开发、Prompt 编写
|
||||
陆刘青 2411020110 前端界面设计、PPT 制作
|
||||
刘俊伯 2411020102 文档撰写、测试与 Bug 修复
|
||||
一个基于Flask的AI面试辅助系统,可以帮助用户模拟面试流程并提供AI分析反馈。
|
||||
|
||||
## 功能特性
|
||||
|
||||
124
app.py
Normal file
124
app.py
Normal file
@ -0,0 +1,124 @@
|
||||
# AI帮你面试 - 核心应用程序
|
||||
from flask import Flask, render_template, request, jsonify
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# 加载环境变量
|
||||
load_dotenv()
|
||||
|
||||
# 创建Flask应用实例
|
||||
app = Flask(__name__)
|
||||
|
||||
# 配置API密钥
|
||||
API_KEY = os.getenv('AI_API_KEY', '') # 从环境变量获取API密钥
|
||||
|
||||
# 定义面试流程数据
|
||||
interview_process = [
|
||||
{"id": 1, "title": "自我介绍", "description": "请简要介绍您自己,包括教育背景和工作经验", "duration": "3分钟"},
|
||||
{"id": 2, "title": "技术能力评估", "description": "回答技术相关问题,展示专业知识", "duration": "15分钟"},
|
||||
{"id": 3, "title": "项目经验分享", "description": "分享您参与的重要项目和成果", "duration": "10分钟"},
|
||||
{"id": 4, "title": "问题与解答", "description": "您可以向面试官提问", "duration": "7分钟"}
|
||||
]
|
||||
|
||||
# API密钥验证装饰器
|
||||
def require_api_key(func):
|
||||
def wrapper(*args, **kwargs):
|
||||
# 从请求头获取API密钥
|
||||
api_key = request.headers.get('X-API-Key')
|
||||
|
||||
# 调试信息(实际生产环境应移除)
|
||||
print(f"[DEBUG] Received API Key: '{api_key}'")
|
||||
print(f"[DEBUG] Received API Key Length: {len(api_key) if api_key else 0}")
|
||||
print(f"[DEBUG] Expected API Key: '{API_KEY}'")
|
||||
print(f"[DEBUG] Expected API Key Length: {len(API_KEY)}")
|
||||
print(f"[DEBUG] Match: {api_key == API_KEY}")
|
||||
|
||||
# 检查是否有不可见字符
|
||||
if api_key:
|
||||
print(f"[DEBUG] Received API Key Hex: {[hex(ord(c)) for c in api_key]}")
|
||||
print(f"[DEBUG] Expected API Key Hex: {[hex(ord(c)) for c in API_KEY]}")
|
||||
|
||||
# 验证API密钥
|
||||
if not api_key or api_key != API_KEY:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': 'Unauthorized: Invalid API Key'
|
||||
}), 401
|
||||
|
||||
return func(*args, **kwargs)
|
||||
|
||||
# 保留原函数的元数据
|
||||
wrapper.__name__ = func.__name__
|
||||
wrapper.__doc__ = func.__doc__
|
||||
return wrapper
|
||||
|
||||
@app.route('/')
|
||||
def home():
|
||||
"""首页路由,显示应用主界面"""
|
||||
return render_template('index.html', app_name='AI帮你面试')
|
||||
|
||||
@app.route('/start_interview')
|
||||
def start_interview():
|
||||
"""开始面试路由,显示面试流程"""
|
||||
return render_template('interview.html', process=interview_process)
|
||||
|
||||
@app.route('/analyze_answer', methods=['POST'])
|
||||
@require_api_key # 添加API密钥验证
|
||||
def analyze_answer():
|
||||
"""AI分析回答的路由"""
|
||||
data = request.get_json()
|
||||
answer = data.get('answer', '')
|
||||
question_id = data.get('question_id', 1)
|
||||
|
||||
# 模拟AI分析过程
|
||||
analysis = {
|
||||
'question_id': question_id,
|
||||
'content_analysis': {
|
||||
'completeness': 85,
|
||||
'relevance': 90,
|
||||
'depth': 75
|
||||
},
|
||||
'sentiment_analysis': {
|
||||
'confidence': 92,
|
||||
'sentiment': 'positive'
|
||||
},
|
||||
'suggestions': [
|
||||
'可以提供更多具体的项目成果数据',
|
||||
'注意控制回答时间,保持简洁明了',
|
||||
'可以结合实例说明技术能力'
|
||||
],
|
||||
'overall_score': 85
|
||||
}
|
||||
|
||||
return jsonify(analysis)
|
||||
|
||||
@app.route('/get_results')
|
||||
@require_api_key # 添加API密钥验证
|
||||
def get_results():
|
||||
"""获取面试结果的路由"""
|
||||
results = {
|
||||
'overall_score': 88,
|
||||
'category_scores': {
|
||||
'professional_knowledge': 90,
|
||||
'communication_skills': 85,
|
||||
'problem_solving': 87,
|
||||
'experience_relevance': 92
|
||||
},
|
||||
'strengths': [
|
||||
'技术知识扎实',
|
||||
'项目经验丰富',
|
||||
'表达清晰流畅'
|
||||
],
|
||||
'improvement_areas': [
|
||||
'可以更详细地描述团队协作经历',
|
||||
'需要加强行业趋势了解',
|
||||
'回答可以更加结构化'
|
||||
],
|
||||
'final_recommendation': '推荐进入下一轮面试'
|
||||
}
|
||||
|
||||
return jsonify(results)
|
||||
|
||||
if __name__ == '__main__':
|
||||
"""应用程序入口点"""
|
||||
app.run(debug=True, host='0.0.0.0', port=5000)
|
||||
43
debug_api_key.py
Normal file
43
debug_api_key.py
Normal file
@ -0,0 +1,43 @@
|
||||
# 直接调试API密钥比较问题
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# 加载环境变量
|
||||
load_dotenv()
|
||||
|
||||
# 从.env获取的密钥
|
||||
api_key_from_env = os.getenv('AI_API_KEY', '')
|
||||
|
||||
# 测试用的密钥(与前端和测试脚本中使用的相同)
|
||||
test_api_key = 'sk-0944c102849e45d9ab3f24d5169de289'
|
||||
|
||||
print(f"API Key from .env: '{api_key_from_env}'")
|
||||
print(f"Test API Key: '{test_api_key}'")
|
||||
print(f"Match: {api_key_from_env == test_api_key}")
|
||||
|
||||
# 检查长度
|
||||
print(f"\nLength from .env: {len(api_key_from_env)}")
|
||||
print(f"Length test key: {len(test_api_key)}")
|
||||
|
||||
# 检查不可见字符
|
||||
print(f"\nHex from .env: {[hex(ord(c)) for c in api_key_from_env]}")
|
||||
print(f"Hex test key: {[hex(ord(c)) for c in test_api_key]}")
|
||||
|
||||
# 检查是否有前后空格
|
||||
print(f"\nStrip from .env: '{api_key_from_env.strip()}'")
|
||||
print(f"Strip test key: '{test_api_key.strip()}'")
|
||||
print(f"Match after strip: {api_key_from_env.strip() == test_api_key.strip()}")
|
||||
|
||||
# 检查是否有换行符或回车符
|
||||
print(f"\nHas newline from .env: {'\n' in api_key_from_env}")
|
||||
print(f"Has newline test key: {'\n' in test_api_key}")
|
||||
print(f"Has carriage return from .env: {'\r' in api_key_from_env}")
|
||||
print(f"Has carriage return test key: {'\r' in test_api_key}")
|
||||
|
||||
# 清除所有空白字符后比较
|
||||
import re
|
||||
clean_env_key = re.sub(r'\s', '', api_key_from_env)
|
||||
clean_test_key = re.sub(r'\s', '', test_api_key)
|
||||
print(f"\nClean from .env: '{clean_env_key}'")
|
||||
print(f"Clean test key: '{clean_test_key}'")
|
||||
print(f"Match after clean: {clean_env_key == clean_test_key}")
|
||||
4
requirements.txt
Normal file
4
requirements.txt
Normal file
@ -0,0 +1,4 @@
|
||||
flask==2.2.2
|
||||
werkzeug==2.2.2
|
||||
python-dotenv==1.0.0
|
||||
requests==2.28.1
|
||||
449
static/script.js
Normal file
449
static/script.js
Normal file
@ -0,0 +1,449 @@
|
||||
// AI帮你面试 - 核心JavaScript功能
|
||||
|
||||
// 全局变量
|
||||
let currentQuestionId = 1;
|
||||
const totalQuestions = 4;
|
||||
|
||||
// API配置
|
||||
const API_CONFIG = {
|
||||
// 开发环境API密钥(实际生产环境应使用更安全的方式管理)
|
||||
apiKey: 'sk-0944c102849e45d9ab3f24d5169de289',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-API-Key': 'sk-0944c102849e45d9ab3f24d5169de289'
|
||||
}
|
||||
};
|
||||
|
||||
// 面试流程数据(与后端保持一致)
|
||||
const interviewProcess = [
|
||||
{"id": 1, "title": "自我介绍", "description": "请简要介绍您自己,包括教育背景和工作经验", "duration": "3分钟"},
|
||||
{"id": 2, "title": "技术能力评估", "description": "回答技术相关问题,展示专业知识", "duration": "15分钟"},
|
||||
{"id": 3, "title": "项目经验分享", "description": "分享您参与的重要项目和成果", "duration": "10分钟"},
|
||||
{"id": 4, "title": "问题与解答", "description": "您可以向面试官提问", "duration": "7分钟"}
|
||||
];
|
||||
|
||||
// 页面加载完成后初始化
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// 初始化面试流程界面
|
||||
initInterviewProcess();
|
||||
|
||||
// 绑定事件监听器
|
||||
bindEventListeners();
|
||||
});
|
||||
|
||||
// 初始化面试流程界面
|
||||
function initInterviewProcess() {
|
||||
// 高亮第一个步骤
|
||||
updateStepHighlight(currentQuestionId);
|
||||
|
||||
// 更新当前问题显示
|
||||
updateCurrentQuestion(currentQuestionId);
|
||||
}
|
||||
|
||||
// 绑定事件监听器
|
||||
function bindEventListeners() {
|
||||
// 分析按钮
|
||||
const analyzeBtn = document.getElementById('analyze-btn');
|
||||
if (analyzeBtn) {
|
||||
analyzeBtn.addEventListener('click', handleAnalyzeAnswer);
|
||||
}
|
||||
|
||||
// 下一题按钮
|
||||
const nextBtn = document.getElementById('next-question-btn');
|
||||
if (nextBtn) {
|
||||
nextBtn.addEventListener('click', handleNextQuestion);
|
||||
}
|
||||
|
||||
// 返回修改按钮
|
||||
const backBtn = document.getElementById('back-to-question-btn');
|
||||
if (backBtn) {
|
||||
backBtn.addEventListener('click', handleBackToQuestion);
|
||||
}
|
||||
|
||||
// 完成面试按钮
|
||||
const finishBtn = document.getElementById('finish-interview-btn');
|
||||
if (finishBtn) {
|
||||
finishBtn.addEventListener('click', handleFinishInterview);
|
||||
}
|
||||
|
||||
// 关闭结果弹窗按钮
|
||||
const closeModalBtn = document.querySelector('.close');
|
||||
if (closeModalBtn) {
|
||||
closeModalBtn.addEventListener('click', closeResultsModal);
|
||||
}
|
||||
|
||||
// 点击弹窗外部关闭
|
||||
const modal = document.getElementById('results-modal');
|
||||
if (modal) {
|
||||
modal.addEventListener('click', function(event) {
|
||||
if (event.target === modal) {
|
||||
closeResultsModal();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 点击流程步骤切换问题
|
||||
const processSteps = document.querySelectorAll('.process-step');
|
||||
processSteps.forEach(step => {
|
||||
step.addEventListener('click', function() {
|
||||
const stepId = parseInt(this.dataset.stepId);
|
||||
if (stepId <= currentQuestionId) {
|
||||
switchToQuestion(stepId);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 处理AI分析回答
|
||||
function handleAnalyzeAnswer() {
|
||||
const answerText = document.getElementById('answer-text').value;
|
||||
|
||||
if (!answerText.trim()) {
|
||||
alert('请先输入您的回答!');
|
||||
return;
|
||||
}
|
||||
|
||||
// 显示加载状态
|
||||
const analyzeBtn = document.getElementById('analyze-btn');
|
||||
const originalText = analyzeBtn.textContent;
|
||||
analyzeBtn.textContent = '分析中...';
|
||||
analyzeBtn.disabled = true;
|
||||
|
||||
// 模拟AI分析过程(实际项目中这里会调用后端API)
|
||||
setTimeout(() => {
|
||||
// 调用AI分析API
|
||||
analyzeAnswerAPI(currentQuestionId, answerText)
|
||||
.then(data => {
|
||||
// 显示分析结果
|
||||
displayAnalysisResults(data);
|
||||
|
||||
// 更新UI
|
||||
document.getElementById('question-stage').classList.add('hidden');
|
||||
document.getElementById('analysis-stage').classList.remove('hidden');
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('分析失败:', error);
|
||||
alert('分析失败,请稍后重试!');
|
||||
})
|
||||
.finally(() => {
|
||||
// 恢复按钮状态
|
||||
analyzeBtn.textContent = originalText;
|
||||
analyzeBtn.disabled = false;
|
||||
});
|
||||
}, 1500);
|
||||
}
|
||||
|
||||
// 调用AI分析API
|
||||
async function analyzeAnswerAPI(questionId, answer) {
|
||||
try {
|
||||
const response = await fetch('/analyze_answer', {
|
||||
method: 'POST',
|
||||
headers: API_CONFIG.headers,
|
||||
body: JSON.stringify({
|
||||
question_id: questionId,
|
||||
answer: answer
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('分析请求失败');
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
console.error('API调用错误:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// 显示AI分析结果
|
||||
function displayAnalysisResults(data) {
|
||||
// 更新综合评分
|
||||
document.getElementById('overall-score').textContent = data.overall_score;
|
||||
|
||||
// 更新内容分析进度条
|
||||
updateProgressBar('completeness-bar', 'completeness-value', data.content_analysis.completeness);
|
||||
updateProgressBar('relevance-bar', 'relevance-value', data.content_analysis.relevance);
|
||||
updateProgressBar('depth-bar', 'depth-value', data.content_analysis.depth);
|
||||
|
||||
// 更新建议列表
|
||||
const suggestionsList = document.getElementById('suggestions-list');
|
||||
suggestionsList.innerHTML = '';
|
||||
|
||||
data.suggestions.forEach(suggestion => {
|
||||
const li = document.createElement('li');
|
||||
li.textContent = suggestion;
|
||||
suggestionsList.appendChild(li);
|
||||
});
|
||||
}
|
||||
|
||||
// 更新进度条
|
||||
function updateProgressBar(barId, valueId, percentage) {
|
||||
const bar = document.getElementById(barId);
|
||||
const value = document.getElementById(valueId);
|
||||
|
||||
bar.style.width = `${percentage}%`;
|
||||
value.textContent = `${percentage}%`;
|
||||
}
|
||||
|
||||
// 处理下一题按钮点击
|
||||
function handleNextQuestion() {
|
||||
if (currentQuestionId < totalQuestions) {
|
||||
currentQuestionId++;
|
||||
switchToQuestion(currentQuestionId);
|
||||
} else {
|
||||
alert('这是最后一个问题!');
|
||||
}
|
||||
}
|
||||
|
||||
// 切换到指定问题
|
||||
function switchToQuestion(questionId) {
|
||||
currentQuestionId = questionId;
|
||||
|
||||
// 更新当前问题显示
|
||||
updateCurrentQuestion(questionId);
|
||||
|
||||
// 更新步骤高亮
|
||||
updateStepHighlight(questionId);
|
||||
|
||||
// 切换到问题阶段
|
||||
document.getElementById('question-stage').classList.remove('hidden');
|
||||
document.getElementById('analysis-stage').classList.add('hidden');
|
||||
|
||||
// 清空回答输入
|
||||
document.getElementById('answer-text').value = '';
|
||||
}
|
||||
|
||||
// 更新当前问题显示
|
||||
function updateCurrentQuestion(questionId) {
|
||||
const question = interviewProcess.find(q => q.id === questionId);
|
||||
if (question) {
|
||||
document.getElementById('current-question-title').textContent = question.title;
|
||||
document.getElementById('current-question-description').textContent = question.description;
|
||||
}
|
||||
}
|
||||
|
||||
// 更新步骤高亮
|
||||
function updateStepHighlight(questionId) {
|
||||
const steps = document.querySelectorAll('.process-step');
|
||||
steps.forEach(step => {
|
||||
const stepId = parseInt(step.dataset.stepId);
|
||||
if (stepId === questionId) {
|
||||
step.classList.add('active');
|
||||
} else if (stepId < questionId) {
|
||||
step.classList.add('completed');
|
||||
step.classList.remove('active');
|
||||
} else {
|
||||
step.classList.remove('active', 'completed');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 处理返回修改按钮
|
||||
function handleBackToQuestion() {
|
||||
document.getElementById('analysis-stage').classList.add('hidden');
|
||||
document.getElementById('question-stage').classList.remove('hidden');
|
||||
}
|
||||
|
||||
// 处理完成面试按钮
|
||||
function handleFinishInterview() {
|
||||
// 显示确认对话框
|
||||
if (confirm('确定要完成面试吗?')) {
|
||||
// 调用获取面试结果API
|
||||
getInterviewResults()
|
||||
.then(results => {
|
||||
displayInterviewResults(results);
|
||||
openResultsModal();
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('获取结果失败:', error);
|
||||
alert('获取面试结果失败,请稍后重试!');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 获取面试结果
|
||||
async function getInterviewResults() {
|
||||
try {
|
||||
const response = await fetch('/get_results', {
|
||||
headers: API_CONFIG.headers
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error('获取结果请求失败');
|
||||
}
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
console.error('API调用错误:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// 显示面试结果
|
||||
function displayInterviewResults(results) {
|
||||
const container = document.getElementById('results-container');
|
||||
|
||||
container.innerHTML = `
|
||||
<div class="score-card">
|
||||
<h3>最终评分</h3>
|
||||
<div class="score-value">${results.overall_score}</div>
|
||||
</div>
|
||||
|
||||
<div class="results-details">
|
||||
<h3>分类评分</h3>
|
||||
<div class="category-scores">
|
||||
<div class="score-item">
|
||||
<label>专业知识</label>
|
||||
<div class="progress">
|
||||
<div class="progress-fill" style="width: ${results.category_scores.professional_knowledge}%"></div>
|
||||
</div>
|
||||
<span>${results.category_scores.professional_knowledge}%</span>
|
||||
</div>
|
||||
|
||||
<div class="score-item">
|
||||
<label>沟通能力</label>
|
||||
<div class="progress">
|
||||
<div class="progress-fill" style="width: ${results.category_scores.communication_skills}%"></div>
|
||||
</div>
|
||||
<span>${results.category_scores.communication_skills}%</span>
|
||||
</div>
|
||||
|
||||
<div class="score-item">
|
||||
<label>解决问题</label>
|
||||
<div class="progress">
|
||||
<div class="progress-fill" style="width: ${results.category_scores.problem_solving}%"></div>
|
||||
</div>
|
||||
<span>${results.category_scores.problem_solving}%</span>
|
||||
</div>
|
||||
|
||||
<div class="score-item">
|
||||
<label>经验相关性</label>
|
||||
<div class="progress">
|
||||
<div class="progress-fill" style="width: ${results.category_scores.experience_relevance}%"></div>
|
||||
</div>
|
||||
<span>${results.category_scores.experience_relevance}%</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="strengths-improvements">
|
||||
<div class="strengths">
|
||||
<h3>👍 优点</h3>
|
||||
<ul>
|
||||
${results.strengths.map(strength => `<li>${strength}</li>`).join('')}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="improvements">
|
||||
<h3>📈 需要改进的地方</h3>
|
||||
<ul>
|
||||
${results.improvement_areas.map(area => `<li>${area}</li>`).join('')}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="final-recommendation">
|
||||
<h3>💡 最终建议</h3>
|
||||
<p class="recommendation-text">${results.final_recommendation}</p>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// 添加结果页面样式
|
||||
addResultsStyles();
|
||||
}
|
||||
|
||||
// 添加结果页面样式
|
||||
function addResultsStyles() {
|
||||
// 动态添加结果页面需要的CSS样式
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
.results-details {
|
||||
margin: 30px 0;
|
||||
}
|
||||
|
||||
.category-scores {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.score-item {
|
||||
background: white;
|
||||
padding: 15px;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 2px 5px rgba(0,0,0,0.05);
|
||||
}
|
||||
|
||||
.score-item label {
|
||||
display: block;
|
||||
margin-bottom: 10px;
|
||||
font-weight: bold;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.strengths-improvements {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 30px;
|
||||
margin: 30px 0;
|
||||
}
|
||||
|
||||
.strengths, .improvements {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 2px 5px rgba(0,0,0,0.05);
|
||||
}
|
||||
|
||||
.strengths h3, .improvements h3 {
|
||||
margin-bottom: 15px;
|
||||
color: #333;
|
||||
border-bottom: 2px solid #eee;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.strengths ul, .improvements ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.strengths li, .improvements li {
|
||||
padding: 10px 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.strengths li:last-child, .improvements li:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.final-recommendation {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
padding: 30px;
|
||||
border-radius: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.final-recommendation h3 {
|
||||
margin-bottom: 20px;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.recommendation-text {
|
||||
font-size: 1.2rem;
|
||||
font-weight: bold;
|
||||
line-height: 1.6;
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
}
|
||||
|
||||
// 打开结果弹窗
|
||||
function openResultsModal() {
|
||||
document.getElementById('results-modal').classList.remove('hidden');
|
||||
}
|
||||
|
||||
// 关闭结果弹窗
|
||||
function closeResultsModal() {
|
||||
document.getElementById('results-modal').classList.add('hidden');
|
||||
}
|
||||
|
||||
567
static/styles.css
Normal file
567
static/styles.css
Normal file
@ -0,0 +1,567 @@
|
||||
/* 全局样式 */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Arial', sans-serif;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: #333;
|
||||
line-height: 1.6;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
/* 通用按钮样式 */
|
||||
.btn {
|
||||
display: inline-block;
|
||||
padding: 10px 20px;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
text-decoration: none;
|
||||
text-align: center;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: linear-gradient(45deg, #667eea, #764ba2);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: #e0e0e0;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background: #bdbdbd;
|
||||
}
|
||||
|
||||
/* 头部样式 */
|
||||
header {
|
||||
background: rgba(255,255,255,0.95);
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
|
||||
margin-bottom: 30px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
font-size: 2.5rem;
|
||||
background: linear-gradient(45deg, #667eea, #764ba2);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 1.2rem;
|
||||
color: #666;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
/* 首页英雄区域 */
|
||||
.hero {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 40px;
|
||||
background: rgba(255,255,255,0.95);
|
||||
padding: 40px;
|
||||
border-radius: 15px;
|
||||
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.hero-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.hero-content h2 {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 20px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.hero-content p {
|
||||
font-size: 1.1rem;
|
||||
margin-bottom: 30px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.features {
|
||||
list-style: none;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.features li {
|
||||
padding: 10px 0;
|
||||
font-size: 1.1rem;
|
||||
color: #555;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.features li:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
/* 面试流程图解 */
|
||||
.hero-image {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.interview-illustration {
|
||||
position: relative;
|
||||
width: 300px;
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
.person, .ai-robot {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.person {
|
||||
background: #667eea;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.ai-robot {
|
||||
background: #4ecdc4;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.chat-bubbles {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.bubble {
|
||||
padding: 15px;
|
||||
border-radius: 20px;
|
||||
margin: 10px 0;
|
||||
max-width: 180px;
|
||||
position: relative;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.person-bubble {
|
||||
background: #667eea;
|
||||
color: white;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.person-bubble::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -8px;
|
||||
left: 20px;
|
||||
border-width: 8px 8px 0;
|
||||
border-style: solid;
|
||||
border-color: #667eea transparent;
|
||||
}
|
||||
|
||||
.ai-bubble {
|
||||
background: #4ecdc4;
|
||||
color: white;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.ai-bubble::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -8px;
|
||||
right: 20px;
|
||||
border-width: 8px 8px 0;
|
||||
border-style: solid;
|
||||
border-color: #4ecdc4 transparent;
|
||||
}
|
||||
|
||||
/* 面试流程页面布局 */
|
||||
.interview-container {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
background: rgba(255,255,255,0.95);
|
||||
padding: 20px;
|
||||
border-radius: 15px;
|
||||
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
/* 侧边栏样式 */
|
||||
.process-sidebar {
|
||||
width: 300px;
|
||||
background: #f8f9fa;
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
height: fit-content;
|
||||
position: sticky;
|
||||
top: 20px;
|
||||
}
|
||||
|
||||
.process-sidebar h2 {
|
||||
margin-bottom: 20px;
|
||||
color: #333;
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
.process-step {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 10px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
background: white;
|
||||
border: 2px solid transparent;
|
||||
}
|
||||
|
||||
.process-step:hover {
|
||||
transform: translateX(5px);
|
||||
box-shadow: 0 5px 15px rgba(0,0,0,0.08);
|
||||
}
|
||||
|
||||
.process-step.active {
|
||||
border-color: #667eea;
|
||||
background: #f0f4ff;
|
||||
}
|
||||
|
||||
.step-number {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 50%;
|
||||
background: #667eea;
|
||||
color: white;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-weight: bold;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.process-step.active .step-number {
|
||||
background: white;
|
||||
color: #667eea;
|
||||
border: 2px solid #667eea;
|
||||
}
|
||||
|
||||
.step-content h3 {
|
||||
font-size: 1.1rem;
|
||||
margin-bottom: 5px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.step-content p {
|
||||
font-size: 0.9rem;
|
||||
color: #666;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.step-duration {
|
||||
font-size: 0.8rem;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* 主面试区域 */
|
||||
.interview-main {
|
||||
flex: 1;
|
||||
background: white;
|
||||
padding: 30px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.interview-stage {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.interview-stage.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#current-question-title {
|
||||
font-size: 1.8rem;
|
||||
margin-bottom: 15px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
#current-question-description {
|
||||
font-size: 1.1rem;
|
||||
margin-bottom: 30px;
|
||||
color: #666;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
/* 回答输入区域 */
|
||||
.answer-input textarea {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
padding: 15px;
|
||||
border: 2px solid #ddd;
|
||||
border-radius: 10px;
|
||||
font-size: 1.1rem;
|
||||
font-family: inherit;
|
||||
resize: vertical;
|
||||
margin-bottom: 20px;
|
||||
transition: border-color 0.3s ease;
|
||||
}
|
||||
|
||||
.answer-input textarea:focus {
|
||||
outline: none;
|
||||
border-color: #667eea;
|
||||
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
|
||||
}
|
||||
|
||||
.input-actions {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
/* AI分析结果区域 */
|
||||
.analysis-results {
|
||||
background: #f8f9fa;
|
||||
padding: 30px;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.score-card {
|
||||
text-align: center;
|
||||
background: white;
|
||||
padding: 30px;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 30px;
|
||||
box-shadow: 0 5px 15px rgba(0,0,0,0.05);
|
||||
}
|
||||
|
||||
.score-card h3 {
|
||||
font-size: 1.2rem;
|
||||
color: #666;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.score-value {
|
||||
font-size: 3rem;
|
||||
font-weight: bold;
|
||||
background: linear-gradient(45deg, #667eea, #764ba2);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
.analysis-details h3, .suggestions h3 {
|
||||
font-size: 1.3rem;
|
||||
margin-bottom: 20px;
|
||||
color: #333;
|
||||
border-bottom: 2px solid #eee;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.progress-bars {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.progress-bar label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
font-weight: bold;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.progress {
|
||||
height: 15px;
|
||||
background: #e0e0e0;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.progress-fill {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, #4ecdc4, #45b7aa);
|
||||
border-radius: 10px;
|
||||
transition: width 0.5s ease;
|
||||
}
|
||||
|
||||
.progress-value {
|
||||
font-size: 0.9rem;
|
||||
color: #666;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.suggestions ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.suggestions li {
|
||||
padding: 12px 20px;
|
||||
margin-bottom: 10px;
|
||||
background: white;
|
||||
border-left: 4px solid #667eea;
|
||||
border-radius: 5px;
|
||||
color: #555;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.suggestions li:hover {
|
||||
transform: translateX(5px);
|
||||
box-shadow: 0 5px 15px rgba(0,0,0,0.05);
|
||||
}
|
||||
|
||||
/* 面试结果弹窗 */
|
||||
.modal {
|
||||
display: flex;
|
||||
position: fixed;
|
||||
z-index: 1000;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0,0,0,0.5);
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.modal.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background: white;
|
||||
padding: 40px;
|
||||
border-radius: 15px;
|
||||
width: 80%;
|
||||
max-width: 600px;
|
||||
max-height: 80vh;
|
||||
overflow-y: auto;
|
||||
position: relative;
|
||||
box-shadow: 0 20px 40px rgba(0,0,0,0.15);
|
||||
}
|
||||
|
||||
.close {
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
top: 20px;
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
color: #aaa;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.close:hover {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* 页脚 */
|
||||
footer {
|
||||
margin-top: 40px;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
background: rgba(255,255,255,0.95);
|
||||
border-radius: 10px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 968px) {
|
||||
.hero {
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.interview-container {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.process-sidebar {
|
||||
width: 100%;
|
||||
position: static;
|
||||
}
|
||||
|
||||
.input-actions {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.input-actions .btn {
|
||||
width: 100%;
|
||||
margin: 5px 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
header {
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.hero {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.hero-content h2 {
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
|
||||
.interview-main {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
#current-question-title {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* 动画效果 */
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.hero, .interview-stage, .process-step {
|
||||
animation: fadeIn 0.5s ease-out;
|
||||
}
|
||||
47
templates/index.html
Normal file
47
templates/index.html
Normal file
@ -0,0 +1,47 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{ app_name }}</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header>
|
||||
<h1>🤖 {{ app_name }}</h1>
|
||||
<p class="subtitle">AI智能面试助手 - 助力求职者成功</p>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<section class="hero">
|
||||
<div class="hero-content">
|
||||
<h2>专业的AI面试辅导</h2>
|
||||
<p>通过智能分析提升您的面试表现,获得针对性的反馈和建议</p>
|
||||
<ul class="features">
|
||||
<li>📋 个性化面试流程设置</li>
|
||||
<li>🧠 AI智能回答分析</li>
|
||||
<li>📊 详细的面试结果评估</li>
|
||||
<li>💡 专业的改进建议</li>
|
||||
</ul>
|
||||
<a href="{{ url_for('start_interview') }}" class="btn btn-primary">开始面试</a>
|
||||
</div>
|
||||
<div class="hero-image">
|
||||
<div class="interview-illustration">
|
||||
<div class="person"></div>
|
||||
<div class="ai-robot"></div>
|
||||
<div class="chat-bubbles">
|
||||
<div class="bubble person-bubble">我有丰富的项目经验...</div>
|
||||
<div class="bubble ai-bubble">很好,请详细说明...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<p>© 2026 {{ app_name }} | 智能面试助手</p>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
116
templates/interview.html
Normal file
116
templates/interview.html
Normal file
@ -0,0 +1,116 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>面试流程 - AI帮你面试</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header>
|
||||
<h1>📋 面试流程</h1>
|
||||
<a href="{{ url_for('home') }}" class="btn btn-secondary">返回首页</a>
|
||||
</header>
|
||||
|
||||
<main class="interview-container">
|
||||
<!-- 面试流程侧边栏 -->
|
||||
<aside class="process-sidebar">
|
||||
<h2>面试环节</h2>
|
||||
<div class="process-steps">
|
||||
{% for step in process %}
|
||||
<div class="process-step" data-step-id="{{ step.id }}">
|
||||
<div class="step-number">{{ step.id }}</div>
|
||||
<div class="step-content">
|
||||
<h3>{{ step.title }}</h3>
|
||||
<p>{{ step.description }}</p>
|
||||
<span class="step-duration">⏱️ {{ step.duration }}</span>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<button id="finish-interview-btn" class="btn btn-primary">完成面试</button>
|
||||
</aside>
|
||||
|
||||
<!-- 主面试区域 -->
|
||||
<section class="interview-main">
|
||||
<div class="interview-stage" id="question-stage">
|
||||
<h2 id="current-question-title">自我介绍</h2>
|
||||
<p id="current-question-description">请简要介绍您自己,包括教育背景和工作经验</p>
|
||||
<div class="answer-input">
|
||||
<textarea id="answer-text" placeholder="请输入您的回答..."></textarea>
|
||||
<div class="input-actions">
|
||||
<button id="analyze-btn" class="btn btn-primary">AI分析</button>
|
||||
<button id="next-question-btn" class="btn btn-secondary">下一题</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- AI分析结果区域 -->
|
||||
<div class="interview-stage hidden" id="analysis-stage">
|
||||
<h2>🤖 AI分析结果</h2>
|
||||
<div class="analysis-results">
|
||||
<div class="score-card">
|
||||
<h3>综合评分</h3>
|
||||
<div class="score-value" id="overall-score">85</div>
|
||||
</div>
|
||||
|
||||
<div class="analysis-details">
|
||||
<h3>内容分析</h3>
|
||||
<div class="progress-bars">
|
||||
<div class="progress-bar">
|
||||
<label>完整性</label>
|
||||
<div class="progress">
|
||||
<div class="progress-fill" id="completeness-bar" style="width: 85%"></div>
|
||||
</div>
|
||||
<span class="progress-value" id="completeness-value">85%</span>
|
||||
</div>
|
||||
|
||||
<div class="progress-bar">
|
||||
<label>相关性</label>
|
||||
<div class="progress">
|
||||
<div class="progress-fill" id="relevance-bar" style="width: 90%"></div>
|
||||
</div>
|
||||
<span class="progress-value" id="relevance-value">90%</span>
|
||||
</div>
|
||||
|
||||
<div class="progress-bar">
|
||||
<label>深度</label>
|
||||
<div class="progress">
|
||||
<div class="progress-fill" id="depth-bar" style="width: 75%"></div>
|
||||
</div>
|
||||
<span class="progress-value" id="depth-value">75%</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="suggestions">
|
||||
<h3>改进建议</h3>
|
||||
<ul id="suggestions-list">
|
||||
<li>可以提供更多具体的项目成果数据</li>
|
||||
<li>注意控制回答时间,保持简洁明了</li>
|
||||
<li>可以结合实例说明技术能力</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<button id="back-to-question-btn" class="btn btn-secondary">返回修改</button>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<!-- 面试结果弹窗 -->
|
||||
<div class="modal hidden" id="results-modal">
|
||||
<div class="modal-content">
|
||||
<span class="close">×</span>
|
||||
<h2>🏆 面试结果</h2>
|
||||
<div id="results-container">
|
||||
<!-- 结果将通过JavaScript动态加载 -->
|
||||
</div>
|
||||
<a href="{{ url_for('home') }}" class="btn btn-primary">返回首页</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="{{ url_for('static', filename='script.js') }}"></script>
|
||||
</body>
|
||||
</html>
|
||||
32
test_api.py
Normal file
32
test_api.py
Normal file
@ -0,0 +1,32 @@
|
||||
import requests
|
||||
import json
|
||||
|
||||
# API测试脚本
|
||||
BASE_URL = 'http://127.0.0.1:5000'
|
||||
TEST_HEADER_URL = 'http://127.0.0.1:5001'
|
||||
API_KEY = 'sk-0944c102849e45d9ab3f24d5169de289'
|
||||
|
||||
# 设置请求头
|
||||
headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'X-API-Key': API_KEY
|
||||
}
|
||||
|
||||
try:
|
||||
print("=== 测试Header调试端点 ===")
|
||||
response = requests.get(f'{TEST_HEADER_URL}/test_headers', headers=headers)
|
||||
print(f"状态码: {response.status_code}")
|
||||
print(f"响应内容: {json.dumps(response.json(), ensure_ascii=False, indent=2)}")
|
||||
|
||||
# 测试分析回答API
|
||||
print("\n=== 测试分析回答API ===")
|
||||
analyze_data = {
|
||||
'question_id': 1,
|
||||
'answer': '我是一名有5年经验的软件工程师,主要从事Python开发工作。'
|
||||
}
|
||||
response = requests.post(f'{BASE_URL}/analyze_answer', headers=headers, json=analyze_data)
|
||||
print(f"状态码: {response.status_code}")
|
||||
print(f"响应内容: {json.dumps(response.json(), ensure_ascii=False, indent=2)}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"测试失败: {str(e)}")
|
||||
31
test_header.py
Normal file
31
test_header.py
Normal file
@ -0,0 +1,31 @@
|
||||
from flask import Flask, request, jsonify
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# 加载环境变量
|
||||
load_dotenv()
|
||||
|
||||
app = Flask(__name__)
|
||||
API_KEY = os.getenv('AI_API_KEY', '')
|
||||
|
||||
@app.route('/test_headers', methods=['GET', 'POST'])
|
||||
def test_headers():
|
||||
print("[DEBUG] All Request Headers:")
|
||||
for key, value in request.headers.items():
|
||||
print(f" {key}: {value}")
|
||||
|
||||
# 特别检查X-API-Key
|
||||
api_key = request.headers.get('X-API-Key')
|
||||
print(f"[DEBUG] X-API-Key specifically: '{api_key}'")
|
||||
print(f"[DEBUG] Expected API Key: '{API_KEY}'")
|
||||
print(f"[DEBUG] Match: {api_key == API_KEY}")
|
||||
|
||||
return jsonify({
|
||||
'received_headers': dict(request.headers),
|
||||
'api_key': api_key,
|
||||
'expected_api_key': API_KEY,
|
||||
'match': api_key == API_KEY
|
||||
})
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(debug=True, port=5001)
|
||||
Loading…
Reference in New Issue
Block a user