feat: 新增崗位描述與清單整合功能 v2.1

主要功能更新:
- 崗位描述保存功能:保存後資料寫入資料庫
- 崗位清單自動刷新:切換模組時自動載入最新資料
- 崗位清單檢視功能:點擊「檢視」按鈕載入對應描述
- 管理者頁面擴充:新增崗位資料管理與匯出功能
- CSV 批次匯入:支援崗位與職務資料批次匯入

後端 API 新增:
- Position Description CRUD APIs
- Position List Query & Export APIs
- CSV Template Download & Import APIs

文件更新:
- SDD.md 更新至版本 2.1
- README.md 更新功能說明與版本歷史

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-12-04 12:46:36 +08:00
parent d17af39bf4
commit b2584772c4
31 changed files with 6795 additions and 365 deletions

159
app_llm_endpoints.py Normal file
View File

@@ -0,0 +1,159 @@
"""
LLM API endpoints to be added to app.py
Add these imports at the top and routes before the error handlers
"""
# ==================== ADD THIS IMPORT AT THE TOP ====================
# from llm_config import LLMConfig
# llm_config = LLMConfig()
# ==================== ADD THESE ROUTES BEFORE ERROR HANDLERS ====================
@app.route('/api/llm/config', methods=['GET'])
def get_llm_config():
"""獲取 LLM API 配置狀態"""
try:
config_data = {}
for api_name, api_config in llm_config.apis.items():
config_data[api_name] = {
'name': api_config['name'],
'enabled': api_config['enabled'],
'endpoint': api_config['endpoint'],
'api_key': api_config['api_key'][:8] + '...' if api_config['api_key'] else ''
}
return jsonify(config_data)
except Exception as e:
return jsonify({
'success': False,
'error': f'獲取配置失敗: {str(e)}'
}), 500
@app.route('/api/llm/test/<api_name>', methods=['GET'])
def test_llm_api(api_name):
"""測試單個 LLM API 連線"""
try:
if api_name not in llm_config.apis:
return jsonify({
'success': False,
'message': f'不支援的 API: {api_name}'
}), 400
# 執行連線測試
if api_name == 'gemini':
success, message = llm_config.test_gemini_connection()
elif api_name == 'deepseek':
success, message = llm_config.test_deepseek_connection()
elif api_name == 'openai':
success, message = llm_config.test_openai_connection()
else:
return jsonify({
'success': False,
'message': f'未實作的 API: {api_name}'
}), 400
return jsonify({
'success': success,
'message': message
})
except Exception as e:
return jsonify({
'success': False,
'message': f'測試失敗: {str(e)}'
}), 500
@app.route('/api/llm/test-all', methods=['GET'])
def test_all_llm_apis():
"""測試所有已配置的 LLM API"""
try:
results = llm_config.test_all_connections()
response = {}
for api_name, (success, message) in results.items():
response[api_name] = {
'success': success,
'message': message
}
return jsonify({
'success': True,
'results': response
})
except Exception as e:
return jsonify({
'success': False,
'error': f'測試失敗: {str(e)}'
}), 500
@app.route('/api/llm/generate', methods=['POST'])
def generate_llm_text():
"""
使用 LLM API 生成文字
Request body: {
"api": "gemini" | "deepseek" | "openai",
"prompt": "提示詞",
"max_tokens": 2000
}
"""
try:
data = request.get_json()
if not data:
return jsonify({
'success': False,
'error': '請提供有效的 JSON 資料'
}), 400
api_name = data.get('api', 'gemini')
prompt = data.get('prompt', '')
max_tokens = data.get('max_tokens', 2000)
if not prompt:
return jsonify({
'success': False,
'error': '請提供提示詞'
}), 400
# 執行文字生成
if api_name == 'gemini':
success, result = llm_config.generate_text_gemini(prompt, max_tokens)
elif api_name == 'deepseek':
success, result = llm_config.generate_text_deepseek(prompt, max_tokens)
elif api_name == 'openai':
model = data.get('model', 'gpt-3.5-turbo')
success, result = llm_config.generate_text_openai(prompt, model, max_tokens)
else:
return jsonify({
'success': False,
'error': f'不支援的 API: {api_name}'
}), 400
if success:
return jsonify({
'success': True,
'text': result
})
else:
return jsonify({
'success': False,
'error': result
}), 500
except Exception as e:
return jsonify({
'success': False,
'error': f'生成失敗: {str(e)}'
}), 500
# ==================== ADD ROUTE FOR API TEST PAGE ====================
@app.route('/api-test')
def api_test_page():
"""返回 API 測試頁面"""
return send_from_directory('.', 'api_test.html')