backup: 完成 HR_position_ 表格前綴重命名與欄位對照表整理

變更內容:
- 所有資料表加上 HR_position_ 前綴
- 整理完整欄位顯示名稱與 ID 對照表
- 模組化 JS 檔案 (admin.js, ai.js, csv.js 等)
- 專案結構優化 (docs/, scripts/, tests/ 等)

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-09 12:05:20 +08:00
parent a068ef9704
commit a6af297623
82 changed files with 8685 additions and 4933 deletions

329
app.py
View File

@@ -27,12 +27,23 @@ except ImportError:
print("Warning: llm_config not found. LLM features will be disabled.")
LLM_ENABLED = False
# Import hierarchy data
try:
from import_hierarchy_data import import_to_memory
hierarchy_data = import_to_memory()
HIERARCHY_ENABLED = True
print("Hierarchy data loaded successfully.")
except Exception as e:
print(f"Warning: Could not load hierarchy data: {e}")
hierarchy_data = None
HIERARCHY_ENABLED = False
app = Flask(__name__, static_folder='.')
app.config['JSON_AS_ASCII'] = False # 確保 JSON 正確處理中文
CORS(app)
# 模擬資料庫 (實際應用中應使用 MySQL/PostgreSQL)
positions_db = {}
HR_position_positions_db = {}
# 預設崗位資料
default_positions = {
@@ -76,10 +87,10 @@ default_positions = {
}
}
positions_db.update(default_positions)
HR_position_positions_db.update(default_positions)
# 職務資料庫
jobs_db = {}
HR_position_jobs_db = {}
# 預設職務資料
default_jobs = {
@@ -102,10 +113,10 @@ default_jobs = {
}
}
jobs_db.update(default_jobs)
HR_position_jobs_db.update(default_jobs)
# 崗位描述資料庫
position_descriptions_db = {}
HR_position_descriptions_db = {}
# 預設崗位描述資料
default_descriptions = {
@@ -123,7 +134,7 @@ default_descriptions = {
}
}
position_descriptions_db.update(default_descriptions)
HR_position_descriptions_db.update(default_descriptions)
# ==================== 靜態頁面 ====================
@@ -267,7 +278,7 @@ def import_positions_csv():
continue
# 檢查是否已存在
if position_code in positions_db:
if position_code in HR_position_positions_db:
errors.append(f"{row_num} 列: 崗位編號 {position_code} 已存在")
error_count += 1
continue
@@ -313,7 +324,7 @@ def import_positions_csv():
'updatedAt': now
}
positions_db[position_code] = new_position
HR_position_positions_db[position_code] = new_position
success_count += 1
except Exception as e:
@@ -349,7 +360,7 @@ def get_positions():
search = request.args.get('search', '', type=str)
# 過濾搜尋
filtered = list(positions_db.values())
filtered = list(HR_position_positions_db.values())
if search:
filtered = [
p for p in filtered
@@ -378,7 +389,7 @@ def get_positions():
@app.route('/api/positions/<position_id>', methods=['GET'])
def get_position(position_id):
"""獲取單一崗位資料"""
if position_id not in positions_db:
if position_id not in HR_position_positions_db:
return jsonify({
'success': False,
'error': '找不到該崗位資料'
@@ -386,7 +397,7 @@ def get_position(position_id):
return jsonify({
'success': True,
'data': positions_db[position_id]
'data': HR_position_positions_db[position_id]
})
@@ -425,7 +436,7 @@ def create_position():
position_code = basic_info['positionCode']
# 檢查是否已存在
if position_code in positions_db:
if position_code in HR_position_positions_db:
return jsonify({
'success': False,
'error': f'崗位編號 {position_code} 已存在'
@@ -441,7 +452,7 @@ def create_position():
'updatedAt': now
}
positions_db[position_code] = new_position
HR_position_positions_db[position_code] = new_position
return jsonify({
'success': True,
@@ -465,7 +476,7 @@ def update_position(position_id):
}
"""
try:
if position_id not in positions_db:
if position_id not in HR_position_positions_db:
return jsonify({
'success': False,
'error': '找不到該崗位資料'
@@ -479,7 +490,7 @@ def update_position(position_id):
}), 400
# 更新資料
existing = positions_db[position_id]
existing = HR_position_positions_db[position_id]
if 'basicInfo' in data:
existing['basicInfo'].update(data['basicInfo'])
@@ -505,13 +516,13 @@ def update_position(position_id):
def delete_position(position_id):
"""刪除崗位資料"""
try:
if position_id not in positions_db:
if position_id not in HR_position_positions_db:
return jsonify({
'success': False,
'error': '找不到該崗位資料'
}), 404
deleted = positions_db.pop(position_id)
deleted = HR_position_positions_db.pop(position_id)
return jsonify({
'success': True,
@@ -532,7 +543,7 @@ def change_position_code(position_id):
Request body: { newCode: "新編號" }
"""
try:
if position_id not in positions_db:
if position_id not in HR_position_positions_db:
return jsonify({
'success': False,
'error': '找不到該崗位資料'
@@ -547,19 +558,19 @@ def change_position_code(position_id):
'error': '請提供新的崗位編號'
}), 400
if new_code in positions_db:
if new_code in HR_position_positions_db:
return jsonify({
'success': False,
'error': f'崗位編號 {new_code} 已存在'
}), 409
# 更新編號
position = positions_db.pop(position_id)
position = HR_position_positions_db.pop(position_id)
position['id'] = new_code
position['basicInfo']['positionCode'] = new_code
position['updatedAt'] = datetime.now().isoformat()
positions_db[new_code] = position
HR_position_positions_db[new_code] = position
return jsonify({
'success': True,
@@ -662,7 +673,7 @@ def import_jobs_csv():
continue
# 檢查是否已存在
if job_code in jobs_db:
if job_code in HR_position_jobs_db:
errors.append(f"{row_num} 列: 職務編號 {job_code} 已存在")
error_count += 1
continue
@@ -687,7 +698,7 @@ def import_jobs_csv():
'updatedAt': now
}
jobs_db[job_code] = new_job
HR_position_jobs_db[job_code] = new_job
success_count += 1
except Exception as e:
@@ -725,7 +736,7 @@ def get_jobs():
category = request.args.get('category', '', type=str)
# 過濾搜尋
filtered = list(jobs_db.values())
filtered = list(HR_position_jobs_db.values())
if search:
filtered = [
j for j in filtered
@@ -759,7 +770,7 @@ def get_jobs():
@app.route('/api/jobs/<job_id>', methods=['GET'])
def get_job(job_id):
"""獲取單一職務資料"""
if job_id not in jobs_db:
if job_id not in HR_position_jobs_db:
return jsonify({
'success': False,
'error': '找不到該職務資料'
@@ -767,7 +778,7 @@ def get_job(job_id):
return jsonify({
'success': True,
'data': jobs_db[job_id]
'data': HR_position_jobs_db[job_id]
})
@@ -814,7 +825,7 @@ def create_job():
job_code = data['jobCode']
# 檢查是否已存在
if job_code in jobs_db:
if job_code in HR_position_jobs_db:
return jsonify({
'success': False,
'error': f'職務編號 {job_code} 已存在'
@@ -829,7 +840,7 @@ def create_job():
'updatedAt': now
}
jobs_db[job_code] = new_job
HR_position_jobs_db[job_code] = new_job
return jsonify({
'success': True,
@@ -847,7 +858,7 @@ def create_job():
def update_job(job_id):
"""更新職務資料"""
try:
if job_id not in jobs_db:
if job_id not in HR_position_jobs_db:
return jsonify({
'success': False,
'error': '找不到該職務資料'
@@ -861,7 +872,7 @@ def update_job(job_id):
}), 400
# 更新資料
existing = jobs_db[job_id]
existing = HR_position_jobs_db[job_id]
existing.update(data)
existing['updatedAt'] = datetime.now().isoformat()
@@ -881,13 +892,13 @@ def update_job(job_id):
def delete_job(job_id):
"""刪除職務資料"""
try:
if job_id not in jobs_db:
if job_id not in HR_position_jobs_db:
return jsonify({
'success': False,
'error': '找不到該職務資料'
}), 404
deleted = jobs_db.pop(job_id)
deleted = HR_position_jobs_db.pop(job_id)
return jsonify({
'success': True,
@@ -908,7 +919,7 @@ def change_job_code(job_id):
Request body: { newCode: "新編號" }
"""
try:
if job_id not in jobs_db:
if job_id not in HR_position_jobs_db:
return jsonify({
'success': False,
'error': '找不到該職務資料'
@@ -923,19 +934,19 @@ def change_job_code(job_id):
'error': '請提供新的職務編號'
}), 400
if new_code in jobs_db:
if new_code in HR_position_jobs_db:
return jsonify({
'success': False,
'error': f'職務編號 {new_code} 已存在'
}), 409
# 更新編號
job = jobs_db.pop(job_id)
job = HR_position_jobs_db.pop(job_id)
job['id'] = new_code
job['jobCode'] = new_code
job['updatedAt'] = datetime.now().isoformat()
jobs_db[new_code] = job
HR_position_jobs_db[new_code] = job
return jsonify({
'success': True,
@@ -1040,14 +1051,14 @@ def get_position_descriptions():
"""獲取所有崗位描述"""
return jsonify({
'success': True,
'data': list(position_descriptions_db.values())
'data': list(HR_position_descriptions_db.values())
})
@app.route('/api/position-descriptions/<position_code>', methods=['GET'])
def get_position_description(position_code):
"""獲取單一崗位描述"""
if position_code not in position_descriptions_db:
if position_code not in HR_position_descriptions_db:
return jsonify({
'success': False,
'error': '找不到該崗位描述'
@@ -1055,7 +1066,7 @@ def get_position_description(position_code):
return jsonify({
'success': True,
'data': position_descriptions_db[position_code]
'data': HR_position_descriptions_db[position_code]
})
@@ -1087,7 +1098,7 @@ def create_position_description():
}), 400
# 檢查崗位是否存在(暫時註解,允許直接新增描述)
# if position_code not in positions_db:
# if position_code not in HR_position_positions_db:
# return jsonify({
# 'success': False,
# 'error': f'崗位編號 {position_code} 不存在,請先建立崗位基礎資料'
@@ -1096,14 +1107,14 @@ def create_position_description():
now = datetime.now().isoformat()
# 如果已存在則更新,否則新增
if position_code in position_descriptions_db:
position_descriptions_db[position_code].update({
if position_code in HR_position_descriptions_db:
HR_position_descriptions_db[position_code].update({
**data,
'updatedAt': now
})
message = '崗位描述更新成功'
else:
position_descriptions_db[position_code] = {
HR_position_descriptions_db[position_code] = {
'id': position_code,
**data,
'createdAt': now,
@@ -1114,7 +1125,7 @@ def create_position_description():
return jsonify({
'success': True,
'message': message,
'data': position_descriptions_db[position_code]
'data': HR_position_descriptions_db[position_code]
})
except Exception as e:
@@ -1128,7 +1139,7 @@ def create_position_description():
def update_position_description(position_code):
"""更新崗位描述"""
try:
if position_code not in position_descriptions_db:
if position_code not in HR_position_descriptions_db:
return jsonify({
'success': False,
'error': '找不到該崗位描述'
@@ -1141,7 +1152,7 @@ def update_position_description(position_code):
'error': '請提供有效的 JSON 資料'
}), 400
position_descriptions_db[position_code].update({
HR_position_descriptions_db[position_code].update({
**data,
'updatedAt': datetime.now().isoformat()
})
@@ -1149,7 +1160,7 @@ def update_position_description(position_code):
return jsonify({
'success': True,
'message': '崗位描述更新成功',
'data': position_descriptions_db[position_code]
'data': HR_position_descriptions_db[position_code]
})
except Exception as e:
@@ -1163,13 +1174,13 @@ def update_position_description(position_code):
def delete_position_description(position_code):
"""刪除崗位描述"""
try:
if position_code not in position_descriptions_db:
if position_code not in HR_position_descriptions_db:
return jsonify({
'success': False,
'error': '找不到該崗位描述'
}), 404
deleted = position_descriptions_db.pop(position_code)
deleted = HR_position_descriptions_db.pop(position_code)
return jsonify({
'success': True,
@@ -1201,8 +1212,8 @@ def get_position_list():
# 組合崗位資料和描述
combined_list = []
for position_code, position in positions_db.items():
description = position_descriptions_db.get(position_code, {})
for position_code, position in HR_position_positions_db.items():
description = HR_position_descriptions_db.get(position_code, {})
combined = {
'positionCode': position_code,
@@ -1214,7 +1225,7 @@ def get_position_list():
'effectiveDate': position['basicInfo'].get('effectiveDate', ''),
'minEducation': position['recruitInfo'].get('minEducation', ''),
'salaryRange': position['recruitInfo'].get('salaryRange', ''),
'hasDescription': position_code in position_descriptions_db,
'hasDescription': position_code in HR_position_descriptions_db,
'jobDuties': description.get('jobDuties', ''),
'requiredSkills': description.get('requiredSkills', ''),
'workEnvironment': description.get('workEnvironment', ''),
@@ -1262,8 +1273,8 @@ def export_position_list():
# 組合所有崗位資料
rows = []
for position_code, position in positions_db.items():
description = position_descriptions_db.get(position_code, {})
for position_code, position in HR_position_positions_db.items():
description = HR_position_descriptions_db.get(position_code, {})
row = [
position_code,
@@ -1477,6 +1488,198 @@ def generate_llm_text():
}), 500
# ==================== 組織階層 API ====================
@app.route('/api/hierarchy/business-units', methods=['GET'])
def get_business_units():
"""獲取所有事業體"""
if not HIERARCHY_ENABLED:
return jsonify({
'success': False,
'error': '組織階層功能未啟用'
}), 503
business_list = [
{'id': v['id'], 'code': v['code'], 'name': v['name']}
for v in hierarchy_data['business_units'].values()
]
business_list.sort(key=lambda x: x['id'])
return jsonify({
'success': True,
'data': business_list
})
@app.route('/api/hierarchy/divisions', methods=['GET'])
def get_divisions():
"""獲取處級單位,可按事業體篩選"""
if not HIERARCHY_ENABLED:
return jsonify({
'success': False,
'error': '組織階層功能未啟用'
}), 503
business_name = request.args.get('business', '', type=str)
if business_name:
# 根據事業體篩選處級單位
division_names = hierarchy_data['businessToDivision'].get(business_name, [])
division_list = [
hierarchy_data['divisions'][name]
for name in division_names
if name in hierarchy_data['divisions']
]
else:
division_list = list(hierarchy_data['divisions'].values())
division_list.sort(key=lambda x: x['id'])
return jsonify({
'success': True,
'data': division_list
})
@app.route('/api/hierarchy/departments', methods=['GET'])
def get_departments():
"""獲取部級單位,可按處級單位篩選"""
if not HIERARCHY_ENABLED:
return jsonify({
'success': False,
'error': '組織階層功能未啟用'
}), 503
division_name = request.args.get('division', '', type=str)
if division_name:
# 根據處級單位篩選部級單位
dept_names = hierarchy_data['divisionToDepartment'].get(division_name, [])
dept_list = [
hierarchy_data['departments'][name]
for name in dept_names
if name in hierarchy_data['departments']
]
else:
dept_list = list(hierarchy_data['departments'].values())
dept_list.sort(key=lambda x: x['id'])
return jsonify({
'success': True,
'data': dept_list
})
@app.route('/api/hierarchy/positions', methods=['GET'])
def get_hierarchy_positions():
"""獲取崗位名稱,可按部級單位篩選"""
if not HIERARCHY_ENABLED:
return jsonify({
'success': False,
'error': '組織階層功能未啟用'
}), 503
department_name = request.args.get('department', '', type=str)
if department_name:
# 根據部級單位篩選崗位
positions = hierarchy_data['departmentToPosition'].get(department_name, [])
else:
# 返回所有不重複的崗位名稱
all_positions = set()
for pos_list in hierarchy_data['departmentToPosition'].values():
all_positions.update(pos_list)
positions = sorted(list(all_positions))
return jsonify({
'success': True,
'data': positions
})
@app.route('/api/hierarchy/full', methods=['GET'])
def get_full_hierarchy():
"""獲取完整階層資料"""
if not HIERARCHY_ENABLED:
return jsonify({
'success': False,
'error': '組織階層功能未啟用'
}), 503
page = request.args.get('page', 1, type=int)
size = request.args.get('size', 50, type=int)
business = request.args.get('business', '', type=str)
division = request.args.get('division', '', type=str)
department = request.args.get('department', '', type=str)
# 篩選資料
filtered = hierarchy_data['organization_positions']
if business:
filtered = [r for r in filtered if r['business'] == business]
if division:
filtered = [r for r in filtered if r['division'] == division]
if department:
filtered = [r for r in filtered if r['department'] == department]
# 分頁
total = len(filtered)
start = (page - 1) * size
end = start + size
paginated = filtered[start:end]
return jsonify({
'success': True,
'data': paginated,
'pagination': {
'page': page,
'size': size,
'total': total,
'totalPages': (total + size - 1) // size
}
})
@app.route('/api/hierarchy/cascade', methods=['GET'])
def get_cascade_data():
"""獲取級聯選擇資料"""
if not HIERARCHY_ENABLED:
return jsonify({
'success': False,
'error': '組織階層功能未啟用'
}), 503
return jsonify({
'success': True,
'data': {
'businessToDivision': hierarchy_data['businessToDivision'],
'divisionToDepartment': hierarchy_data['divisionToDepartment'],
'departmentToPosition': hierarchy_data['departmentToPosition']
}
})
@app.route('/api/hierarchy/stats', methods=['GET'])
def get_hierarchy_stats():
"""獲取組織階層統計"""
if not HIERARCHY_ENABLED:
return jsonify({
'success': False,
'error': '組織階層功能未啟用'
}), 503
return jsonify({
'success': True,
'data': {
'businessUnitsCount': len(hierarchy_data['business_units']),
'divisionsCount': len(hierarchy_data['divisions']),
'departmentsCount': len(hierarchy_data['departments']),
'organizationPositionsCount': len(hierarchy_data['organization_positions'])
}
})
# ==================== 錯誤處理 ====================
@app.errorhandler(404)
@@ -1529,6 +1732,15 @@ if __name__ == '__main__':
║ GET /api/llm/test-all - 測試所有 API ║
║ POST /api/llm/generate - 生成文字 ║
║ ║
║ 組織階層 API: ║
║ GET /api/hierarchy/business-units - 獲取事業體 ║
║ GET /api/hierarchy/divisions - 獲取處級單位 ║
║ GET /api/hierarchy/departments - 獲取部級單位 ║
║ GET /api/hierarchy/positions - 獲取崗位名稱 ║
║ GET /api/hierarchy/full - 完整階層資料 ║
║ GET /api/hierarchy/cascade - 級聯選擇資料 ║
║ GET /api/hierarchy/stats - 組織統計 ║
║ ║
║ 按 Ctrl+C 停止伺服器 ║
╚══════════════════════════════════════════════════════════════╝
""")
@@ -1543,5 +1755,14 @@ if __name__ == '__main__':
else:
print("[!] LLM 功能未啟用 (llm_config.py 未找到)")
if HIERARCHY_ENABLED:
print("[OK] 組織階層功能已啟用")
print(f" - 事業體: {len(hierarchy_data['business_units'])}")
print(f" - 處級單位: {len(hierarchy_data['divisions'])}")
print(f" - 部級單位: {len(hierarchy_data['departments'])}")
print(f" - 組織崗位關聯: {len(hierarchy_data['organization_positions'])}")
else:
print("[!] 組織階層功能未啟用")
print()
app.run(host='0.0.0.0', port=5000, debug=True)