diff --git a/.env b/.env
new file mode 100644
index 0000000..b7448d8
--- /dev/null
+++ b/.env
@@ -0,0 +1,7 @@
+# AI帮你面试 - 环境配置
+
+# API密钥配置
+AI_API_KEY=sk-0944c102849e45d9ab3f24d5169de289
+
+# Flask应用配置
+FLASK_DEBUG=True
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e69de29
diff --git a/README.md b/README.md
index 890dfc5..7c11a52 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,8 @@
# AI帮你面试
-
+姓名 学号 主要贡献 (具体分工)
+蔡朗 2411020227 核心逻辑开发、Prompt 编写
+陆刘青 2411020110 前端界面设计、PPT 制作
+刘俊伯 2411020102 文档撰写、测试与 Bug 修复
一个基于Flask的AI面试辅助系统,可以帮助用户模拟面试流程并提供AI分析反馈。
## 功能特性
diff --git a/app.py b/app.py
new file mode 100644
index 0000000..10dad20
--- /dev/null
+++ b/app.py
@@ -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)
\ No newline at end of file
diff --git a/debug_api_key.py b/debug_api_key.py
new file mode 100644
index 0000000..e57b59f
--- /dev/null
+++ b/debug_api_key.py
@@ -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}")
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..de4e210
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,4 @@
+flask==2.2.2
+werkzeug==2.2.2
+python-dotenv==1.0.0
+requests==2.28.1
\ No newline at end of file
diff --git a/static/script.js b/static/script.js
new file mode 100644
index 0000000..4d637cd
--- /dev/null
+++ b/static/script.js
@@ -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 = `
+
+
最终评分
+
${results.overall_score}
+
+
+
+
分类评分
+
+
+
+
+
${results.category_scores.professional_knowledge}%
+
+
+
+
+
+
${results.category_scores.communication_skills}%
+
+
+
+
+
+
${results.category_scores.problem_solving}%
+
+
+
+
+
+
${results.category_scores.experience_relevance}%
+
+
+
+
+
+
+
👍 优点
+
+ ${results.strengths.map(strength => `- ${strength}
`).join('')}
+
+
+
+
+
📈 需要改进的地方
+
+ ${results.improvement_areas.map(area => `- ${area}
`).join('')}
+
+
+
+
+
+
💡 最终建议
+
${results.final_recommendation}
+
+ `;
+
+ // 添加结果页面样式
+ 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');
+}
+
diff --git a/static/styles.css b/static/styles.css
new file mode 100644
index 0000000..13c5690
--- /dev/null
+++ b/static/styles.css
@@ -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;
+}
\ No newline at end of file
diff --git a/templates/index.html b/templates/index.html
new file mode 100644
index 0000000..13d6ef9
--- /dev/null
+++ b/templates/index.html
@@ -0,0 +1,47 @@
+
+
+
+
+
+ {{ app_name }}
+
+
+
+
+
+ 🤖 {{ app_name }}
+ AI智能面试助手 - 助力求职者成功
+
+
+
+
+
+
专业的AI面试辅导
+
通过智能分析提升您的面试表现,获得针对性的反馈和建议
+
+ - 📋 个性化面试流程设置
+ - 🧠 AI智能回答分析
+ - 📊 详细的面试结果评估
+ - 💡 专业的改进建议
+
+
开始面试
+
+
+
+
+
+
+
我有丰富的项目经验...
+
很好,请详细说明...
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/templates/interview.html b/templates/interview.html
new file mode 100644
index 0000000..e5a6353
--- /dev/null
+++ b/templates/interview.html
@@ -0,0 +1,116 @@
+
+
+
+
+
+ 面试流程 - AI帮你面试
+
+
+
+
+
+
+
+
+
+
+
+
+
+
自我介绍
+
请简要介绍您自己,包括教育背景和工作经验
+
+
+
+
+
+
🤖 AI分析结果
+
+
+
+
+
+
+
改进建议
+
+ - 可以提供更多具体的项目成果数据
+ - 注意控制回答时间,保持简洁明了
+ - 可以结合实例说明技术能力
+
+
+
+
+
+
+
+
+
+
+
+
×
+
🏆 面试结果
+
+
+
+
返回首页
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test_api.py b/test_api.py
new file mode 100644
index 0000000..69d40fd
--- /dev/null
+++ b/test_api.py
@@ -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)}")
diff --git a/test_header.py b/test_header.py
new file mode 100644
index 0000000..69abb20
--- /dev/null
+++ b/test_header.py
@@ -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)