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

258
add_random_positions.py Normal file
View File

@@ -0,0 +1,258 @@
"""
隨機建立 10 筆崗位資料到系統
從 excel_table copy.md 中隨機選取資料並透過 API 建立
"""
import requests
import random
from datetime import datetime
import sys
import io
# 設定 UTF-8 輸出
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
# 從 excel_table copy.md 中讀取的組織及崗位資料
org_positions = [
{"business": "岡山製造事業體", "division": "生產處", "department": "生產部", "position": "課長"},
{"business": "岡山製造事業體", "division": "生產處", "department": "生產部", "position": "組長"},
{"business": "岡山製造事業體", "division": "生產處", "department": "生產部", "position": "班長"},
{"business": "岡山製造事業體", "division": "生產處", "department": "生產部", "position": "副班長"},
{"business": "岡山製造事業體", "division": "生產處", "department": "生產部", "position": "作業員"},
{"business": "岡山製造事業體", "division": "生產處", "department": "生產企劃部", "position": "經副理"},
{"business": "岡山製造事業體", "division": "生產處", "department": "生產企劃部", "position": "課長"},
{"business": "岡山製造事業體", "division": "生產處", "department": "生產企劃部", "position": "專員"},
{"business": "岡山製造事業體", "division": "生產處", "department": "生產企劃部", "position": "工程師"},
{"business": "岡山製造事業體", "division": "岡山製造事業體", "department": "岡山品質管制部", "position": "經副理"},
{"business": "岡山製造事業體", "division": "岡山製造事業體", "department": "岡山品質管制部", "position": "課長"},
{"business": "岡山製造事業體", "division": "岡山製造事業體", "department": "岡山品質管制部", "position": "工程師"},
{"business": "岡山製造事業體", "division": "岡山製造事業體", "department": "岡山品質管制部", "position": "組長"},
{"business": "岡山製造事業體", "division": "岡山製造事業體", "department": "岡山品質管制部", "position": "班長"},
{"business": "岡山製造事業體", "division": "岡山製造事業體", "department": "岡山品質管制部", "position": "副班長"},
{"business": "岡山製造事業體", "division": "岡山製造事業體", "department": "岡山品質管制部", "position": "作業員"},
{"business": "岡山製造事業體", "division": "封裝工程處", "department": "", "position": "處長"},
{"business": "岡山製造事業體", "division": "封裝工程處", "department": "", "position": "專員"},
{"business": "岡山製造事業體", "division": "封裝工程處", "department": "", "position": "工程師"},
{"business": "岡山製造事業體", "division": "封裝工程處", "department": "製程工程一部", "position": "經副理"},
{"business": "岡山製造事業體", "division": "封裝工程處", "department": "製程工程二部", "position": "經副理"},
{"business": "岡山製造事業體", "division": "封裝工程處", "department": "製程工程二部", "position": "課長"},
{"business": "岡山製造事業體", "division": "封裝工程處", "department": "製程工程二部", "position": "工程師"},
{"business": "岡山製造事業體", "division": "封裝工程處", "department": "設備一部", "position": "經副理"},
{"business": "岡山製造事業體", "division": "封裝工程處", "department": "設備二部", "position": "經副理"},
{"business": "岡山製造事業體", "division": "封裝工程處", "department": "設備二部", "position": "課長"},
{"business": "岡山製造事業體", "division": "封裝工程處", "department": "設備二部", "position": "工程師"},
{"business": "岡山製造事業體", "division": "副總辦公室", "department": "工業工程部", "position": "經副理"},
{"business": "岡山製造事業體", "division": "副總辦公室", "department": "工業工程部", "position": "工程師"},
{"business": "岡山製造事業體", "division": "副總辦公室", "department": "工業工程部", "position": "課長"},
{"business": "岡山製造事業體", "division": "副總辦公室", "department": "工業工程部", "position": "副理"},
{"business": "岡山製造事業體", "division": "測試工程與研發處", "department": "", "position": "處長"},
{"business": "岡山製造事業體", "division": "測試工程與研發處", "department": "", "position": "專員"},
{"business": "岡山製造事業體", "division": "測試工程與研發處", "department": "測試工程部", "position": "經副理"},
{"business": "岡山製造事業體", "division": "測試工程與研發處", "department": "測試工程部", "position": "課長"},
{"business": "岡山製造事業體", "division": "測試工程與研發處", "department": "測試工程部", "position": "工程師"},
{"business": "岡山製造事業體", "division": "測試工程與研發處", "department": "新產品導入部", "position": "經副理"},
{"business": "岡山製造事業體", "division": "測試工程與研發處", "department": "新產品導入部", "position": "專員"},
{"business": "岡山製造事業體", "division": "測試工程與研發處", "department": "新產品導入部", "position": "工程師"},
{"business": "岡山製造事業體", "division": "測試工程與研發處", "department": "研發部", "position": "經副理"},
{"business": "產品事業體", "division": "先進產品事業處", "department": "產品管理部(APD)", "position": "經副理"},
{"business": "產品事業體", "division": "先進產品事業處", "department": "產品管理部(APD)", "position": "工程師"},
{"business": "產品事業體", "division": "成熟產品事業處", "department": "產品管理部(MPD)", "position": "經副理"},
{"business": "產品事業體", "division": "成熟產品事業處", "department": "產品管理部(MPD)", "position": "專案經副理"},
{"business": "產品事業體", "division": "成熟產品事業處", "department": "產品管理部(MPD)", "position": "工程師"},
{"business": "晶圓三廠", "division": "晶圓三廠", "department": "品質部", "position": "經副理"},
{"business": "晶圓三廠", "division": "晶圓三廠", "department": "品質部", "position": "工程師"},
{"business": "晶圓三廠", "division": "晶圓三廠", "department": "製造部", "position": "經副理"},
{"business": "晶圓三廠", "division": "晶圓三廠", "department": "製造部", "position": "課長"},
{"business": "晶圓三廠", "division": "晶圓三廠", "department": "製造部", "position": "班長"},
{"business": "晶圓三廠", "division": "製程工程處", "department": "工程一部", "position": "經副理"},
{"business": "晶圓三廠", "division": "製程工程處", "department": "工程一部", "position": "工程師"},
{"business": "晶圓三廠", "division": "製程工程處", "department": "工程二部", "position": "經副理"},
{"business": "晶圓三廠", "division": "製程工程處", "department": "工程二部", "position": "工程師"},
{"business": "集團人資行政事業體", "division": "集團人資行政事業體", "department": "招募任用部", "position": "經副理"},
{"business": "集團人資行政事業體", "division": "集團人資行政事業體", "department": "招募任用部", "position": "專員"},
{"business": "集團人資行政事業體", "division": "集團人資行政事業體", "department": "訓練發展部", "position": "經副理"},
{"business": "集團人資行政事業體", "division": "集團人資行政事業體", "department": "訓練發展部", "position": "專員"},
{"business": "集團人資行政事業體", "division": "集團人資行政事業體", "department": "薪酬管理部", "position": "經副理"},
{"business": "集團人資行政事業體", "division": "集團人資行政事業體", "department": "薪酬管理部", "position": "專員"},
]
# 崗位類別對應
position_category_map = {
"處長": "02", # 管理職
"經副理": "02",
"副理": "02",
"課長": "02",
"組長": "02",
"班長": "02",
"副班長": "02",
"工程師": "01", # 技術職
"專員": "04", # 行政職
"作業員": "06", # 生產職
}
# 崗位等級對應
position_level_map = {
"處長": "L7",
"經副理": "L5",
"副理": "L5",
"課長": "L4",
"組長": "L3",
"班長": "L3",
"副班長": "L2",
"工程師": "L3",
"專員": "L3",
"作業員": "L1",
}
# 學歷要求對應
education_map = {
"處長": "MA", # 碩士
"經副理": "BA", # 大學
"副理": "BA",
"課長": "BA",
"組長": "JC", # 專科
"班長": "JC",
"副班長": "HS", # 高中職
"工程師": "BA",
"專員": "BA",
"作業員": "HS",
}
# 薪資範圍對應
salary_range_map = {
"處長": "E",
"經副理": "D",
"副理": "D",
"課長": "C",
"組長": "C",
"班長": "B",
"副班長": "B",
"工程師": "C",
"專員": "C",
"作業員": "A",
}
def generate_position_code(business, division, department, position, index):
"""生成崗位編號"""
# 事業體代碼
business_code_map = {
"岡山製造事業體": "KS",
"產品事業體": "PD",
"晶圓三廠": "F3",
"集團人資行政事業體": "HR",
}
# 崗位代碼
position_code_map = {
"處長": "DIR",
"經副理": "MGR",
"副理": "AMG",
"課長": "SUP",
"組長": "LDR",
"班長": "CHF",
"副班長": "ACF",
"工程師": "ENG",
"專員": "SPC",
"作業員": "OPR",
}
biz_code = business_code_map.get(business, "XX")
pos_code = position_code_map.get(position, "XXX")
return f"{biz_code}-{pos_code}-{index:03d}"
def create_position_data(org_data, index):
"""創建崗位資料"""
business = org_data["business"]
division = org_data["division"]
department = org_data["department"]
position = org_data["position"]
position_code = generate_position_code(business, division, department, position, index)
# 組合崗位名稱
dept_str = f"{department}-" if department else ""
position_name = f"{dept_str}{position}"
return {
"basicInfo": {
"positionCode": position_code,
"positionName": position_name,
"positionCategory": position_category_map.get(position, "04"),
"positionCategoryName": "管理職" if position_category_map.get(position, "04") == "02" else "技術職",
"positionNature": "FT",
"positionNatureName": "全職",
"headcount": str(random.randint(1, 5)),
"positionLevel": position_level_map.get(position, "L3"),
"effectiveDate": "2024-01-01",
"positionDesc": f"{business} {division} {position_name}",
"positionRemark": f"組織架構: {business} > {division} > {department if department else '(處級)'}"
},
"recruitInfo": {
"minEducation": education_map.get(position, "BA"),
"requiredGender": "",
"salaryRange": salary_range_map.get(position, "C"),
"workExperience": str(random.randint(0, 5)),
"minAge": "22",
"maxAge": "50",
"jobType": "FT",
"recruitPosition": position,
"jobTitle": position,
"jobDesc": "",
"positionReq": "",
"titleReq": "",
"majorReq": "",
"skillReq": "",
"langReq": "",
"otherReq": "",
"superiorPosition": "",
"recruitRemark": ""
}
}
def main():
"""主程式"""
api_url = "http://localhost:5000/api/positions"
# 隨機選取 10 筆資料
selected = random.sample(org_positions, min(10, len(org_positions)))
print("=" * 60)
print("隨機建立 10 筆崗位資料")
print("=" * 60)
print()
success_count = 0
fail_count = 0
for i, org_data in enumerate(selected, 1):
position_data = create_position_data(org_data, i)
print(f"[{i}/10] 建立崗位: {position_data['basicInfo']['positionCode']} - {position_data['basicInfo']['positionName']}")
try:
response = requests.post(api_url, json=position_data, timeout=5)
if response.status_code == 201:
print(f" [OK] 成功")
success_count += 1
else:
error_msg = response.json().get('error', '未知錯誤')
print(f" [ERROR] 失敗: {error_msg}")
fail_count += 1
except requests.exceptions.ConnectionError:
print(f" [ERROR] 失敗: 無法連接到伺服器 (請確認伺服器是否已啟動)")
fail_count += 1
except Exception as e:
print(f" [ERROR] 失敗: {str(e)}")
fail_count += 1
print()
print("=" * 60)
print(f"建立完成: 成功 {success_count} 筆, 失敗 {fail_count}")
print("=" * 60)
if __name__ == "__main__":
main()