應用情境 - 組織人員同步
「維護組織人員」功能用於快速更新組織架構及人員資訊。在整合 UOF X 與外部系統的過程中,可能出現兩邊系統的組織資料不同步的情況,因此,UOF X 提供了此功能,讓公司能夠有效解決資料不同步的問題,確保與外部系統資料的一致性。該功能支援建立組織架構及同步組織人員資訊,以下會展示一個應用情境:「組織人員同步」,並提供具體的實作步驟與方法作為參考。
應用情境¶
公司 A 最近導入 UOF X 系統,但組織架構與人員資訊以 HR 系統為主,公司 A 希望能保持在 HR 系統維護組織人員資訊,再將資料同步到 UOF X 上,確保兩個系統的資料一致。
面對隨時異動且大量的組織與人員資料,如果依賴手動輸入,不僅耗費時間與人力,還容易出現資料遺漏與輸入錯誤的風險。
透過 UOF X 提供的 SDK,我們可以撰寫出「組織人員同步」功能,公司能快速進行批次資料的同步,避免手動操作的繁瑣,確保資料的正確性與一致性。
UOF X 與外部系統組織人員同步優點¶
- 資料同步與一致性:確保 UOF X 與外部系統中的組織架構及人員資訊保持一致,避免因資料不同步導致的管理混亂。
- 提升管理效率:支援自動同步與批次更新,減少重複輸入的工作,降低人為錯誤風險。
- 即時更新組織變動:當組織調整或人事異動時,可透過 UOF X 快速反映變動,確保內部系統即時更新。
- 減少人力與時間成本:自動化的同步流程降低人力需求,提升資料維護效率,節省大量時間。
- 降低資料不一致風險:整合過程中即時檢查與更新,避免資料不一致對業務流程造成的影響。
同步規則¶
在進行同步前,必須先處理欄位對應、資料格式轉換以及定義出同步規則,以確保 HR 系統與 UOF X 之間的資料正確無誤。
程式邏輯說明¶
- 雙方系統人員及部門資料以人員帳號 Account 及部門代號 Code 作為比對的依據
部門同步流程圖
flowchart LR
A(一、處理外部系統資料) --> B(欄位名稱、資料轉換) & C(建立部門階層<br>-非必要-) --> D(二、與 UOF X 比對資料)
D --> E(更新 UOF X 部門)
D --> F(新增 UOF X 部門)
D --> G(停用 UOF X 部門)
- 部門同步規則:
- UOF X「部門層級」資料依據 HR 系統的「部門代號」及「父部門代號」資料建立部門階層結構
- 部門階層同時存在於 HR 系統與 UOF X,更新 UOF X 該部門階層
- 部門階層存在於 HR 系統,而不存在於 UOF X,將該部門階層新增到 UOF X
- 部門階層不存在於 HR 系統,在 UOF X 仍存在,停用 UOF X 該部門階層
- 部門同時存在於 HR 系統與 UOF X,更新 UOF X 該部門
- 部門存在於 HR 系統,而不存在於 UOF X,將該部門新增到 UOF X
- 部門不存在於 HR 系統,在 UOF X 仍存在,停用 UOF X 該部門
人員同步流程圖
flowchart LR
A(一、處理外部系統資料) --> B(欄位名稱、資料轉換) & C(同步職稱職務) --> D(二、與 UOF X 比對資料)
D --> E(更新 UOF X 人員)
D --> F(新增 UOF X 人員)
D --> G(停用 UOF X 人員)
E & F & G --> H(三、同步主管)
- 人員同步規則:
- 同步職稱職務:
- 若「職務」不存在 UOF X,新增該職務到 UOF X
- 若「職稱」不存在 UOF X,新增該職稱到 UOF X
- 人員同時存在於 HR 系統與 UOF X,更新 UOF X 該人員
- 人員存在於 HR 系統,而不存在於 UOF X,將該人員新增到 UOF X
- 同步職稱職務:
- 現在日期大於帳號過期時間,將該人員帳號設為停用
新增職稱設定
因為該範例無法從職稱的字面上判斷出「簽何層級」,因此將簽核層級預設為 50,後續再由管理員到 UOF X 進行調整
Usertype
因為人員帳號以 Account
作為比對的依據,因此統一將 UserType
設為 Account
Supervisortype
因為 SupervisorType
沒有對應的欄位,因此將其預設為 DeptManager
欄位對應¶
人員欄位對應:
UOF X 欄位 | HR 系統欄位 | 說明 |
---|---|---|
UserType |
(無對應欄位) | 人員的類別 (帳號 Account , 員編 EmployeeNo ) |
UserCode |
(無對應欄位) | 帳號或員編 |
Account |
Account |
人員帳號 |
Name |
Name |
中文姓名 |
EnglishName |
EnglishName |
英文姓名 |
EmployeeNumber |
EmployeeID |
員工編號 |
ExpiredTime |
ExpiredTime |
帳號過期時間 |
Gender |
Gender |
性別 ( UOF X: 0 , 1 , 2 / HR : male , female , other ) |
IdCardNumber |
IDNumber |
身份證字號 |
BirthDate |
BirthDate |
生日 ( UOF X: 西元 / HR : 民國) |
PhoneNumber |
PhoneNumber |
行動電話 |
BusinessCard |
CardTitle |
名片職稱 |
HireDate |
HireDate |
到職日 |
ResignationDate |
ResignationDate |
離職日 |
Email |
PrimaryEmail |
主要信箱 |
EmailEx |
SecondaryEmail |
其他信箱 |
Locked |
AccountLocked |
是否鎖定人員帳號 |
Active |
AccountActive |
是否停用人員帳號 |
SupervisorType |
(無對應欄位) | 主管類別 (依部門主管 DeptManager , 依自訂簽核主管 Customize ) |
SupervisorAccount |
(無對應欄位) | 當主管類別為「依自訂簽核主管」時,需設定主管帳號 |
JobTitleCode |
JobTitleID |
職稱代號 |
JobFuncs |
JobFunctions |
職務代號 (可多筆) |
DeptCode |
DeptCode |
部門代號 |
IsMainDept |
PrimaryDepartment |
是否為主要部門 |
(無對應欄位) | IsSupervisor |
是否為主管 |
部門的欄位對應:
UOF X 欄位 | HR 系統欄位 | 說明 |
---|---|---|
Code |
DeptCode |
部門代號 |
Name |
DeptName |
部門名稱 |
Seq |
Order |
部門的排序 (從 0 開始) |
Active |
IsActive |
是否啟用部門 |
IncludeSubDept |
HasSubDepartments |
是否包含子部門 |
ParentCode |
ParentDeptCode |
父部門代號 |
DeptLevelCode |
(無對應欄位) | 部門層級代號 |
Description |
DeptDescription |
描述 |
實作¶
在繼續往下說明之前,請確認您下列事項皆已經準備完成:
- 透過 Visual Studio 建立一個新的主控台應用程式 (Console App)
- 已將專案透過 NuGet 安裝 SDK,並設定好 SDK 參數
- 已取得 UOF X
站台網址
- 已取得
金鑰
完整程式範例¶
以下為此情境中組織人員同步的完整程式範例,後續會針對同步部門 與同步人員兩大部分做詳細說明。
using Ede.Uofx.PubApi.Sdk.NetStd;
using Ede.Uofx.PubApi.Sdk.NetStd.Models.Base;
using Ede.Uofx.PubApi.Sdk.NetStd.Service;
//設定金鑰
UofxService.Key = "xxx";
//設定 UOF X 站台網址
UofxService.UofxServerUrl = "https://myuofx.com.tw";
// 儲存成功訊息
List<string> successMsg = new List<string> { "====================================================同步成功====================================================" };
// 儲存錯誤訊息
List<string> errorMsg = new List<string> { "====================================================同步失敗====================================================" };
// (1)!
// HR 系統部門資料
List<HrDeptModel> originHrDepts = new List<HrDeptModel>{...};
// HR 系統人員資料
List<HrEmplModel> originHrEmpls = new List<HrEmplModel>{...};
#region 主程式
try
{
// 同步人員部門資料
await SyncDepts(originHrDepts);
await SyncEmpls(originHrEmpls);
// 列印錯誤成功訊息
ConsoleList(errorMsg);
ConsoleList(successMsg);
}
catch (Exception ex)
{
//將 exception 轉換成較容易判斷的 model
var model = UofxService.Error.ConvertToModel(ex);
//將 model 轉成 json 格式印出
Console.WriteLine(UofxService.Json.Convert(model));
}
#endregion
// 列印錯誤成功訊息
void ConsoleList(List<string> list)
{
foreach (var item in list)
{
Console.WriteLine(item);
}
};
#region 同步部門
// 同步部門資料
async Task SyncDepts(List<HrDeptModel> originHrDepts)
{
// 部門資料階層化
var hierarchyDeptList = BuildHierarchy(originHrDepts);
// 建立部門層級與轉換後的部門 List
var (deptLevList, deptList) = BuildDeptLevs(hierarchyDeptList, 1);
// 比對與同步部門階層資料
await MapDeptLevels(deptLevList);
// 比對與同步部門資料
await MapDepts(deptList);
};
// 部門資料階層化
List<HrDeptModel> BuildHierarchy(List<HrDeptModel> originHrDepts)
{
// 取得有子部門的父部門資料
var getHRParentDeptList = originHrDepts.Where(x => x.HasSubDepartments).ToList();
// 將子部門資料加入父部門
foreach (var dept in getHRParentDeptList)
{
var getSubDept = originHrDepts.Where(x => x.ParentDeptCode == dept.DeptCode).ToList();
dept.SubDepts = getSubDept;
};
// 取得沒有父部門的部門資料
var hierarchyDeptList = originHrDepts.Where(x => string.IsNullOrEmpty(x.ParentDeptCode)).ToList();
return hierarchyDeptList;
};
// 建立部門層級與轉換後的部門 List
(List<DeptLevelViewModel> deptLevList, List<DeptModel> deptList) BuildDeptLevs(List<HrDeptModel> hierarchy, int level)
{
// 部門層級 List
var deptLevList = new List<DeptLevelViewModel>();
// 部門 List
var deptList = new List<DeptModel>();
foreach (var dept in hierarchy)
{
// 確認不新增重複部門層級
if (!deptLevList.Any(x => x.Seq == level))
{
// 新增當前層級的資料
deptLevList.Add(new DeptLevelViewModel
{
Code = $"lev{level}", // 部門層級代號
Name = $"Level{level}", // 部門層級名稱
Seq = level, // 部門層級的階層與排序 ( 從 1 開始 )
CorpCode = _corpCode, // 公司代號
Active = true // 是否啟用部門層級
});
}
// 轉換部門資料並加入部門層級
deptList.Add(new DeptModel
{
Code = dept.DeptCode, // 部門代號
Name = dept.DeptName, // 部門名稱
Active = true, // 是否啟用部門
IncludeSubDept = dept.HasSubDepartments, // 是否包含子部門
ParentCode = dept.ParentDeptCode, // 父部門代號 ( 可設定為空值,代表無父部門 )
Description = dept.DeptDescription, // 描述
DeptLevelCode = $"lev{level}", // 部門層級代號
Seq = dept.Order - 1 // 部門的排序 ( 從 0 開始 )
});
// 遞迴處理子部門,層級加 1,並將結果加入 deptLevList 與 deptList
if (dept.SubDepts != null && dept.SubDepts.Count > 0)
{
var (deptLevListTemp, deptListTemp) = BuildDeptLevs(dept.SubDepts, level + 1);
deptLevList.AddRange(deptLevListTemp);
deptList.AddRange(deptListTemp);
}
};
return (deptLevList, deptList);
};
// 比對與同步部門階層資料
async Task MapDeptLevels(List<DeptLevelViewModel> hrDeptLevs)
{
// 更新 List
var deptLevelToUpdate = new List<DeptLevelViewModel>();
// 新增 List
var deptLevelToAdd = new List<DeptLevelViewModel>();
// 停用 List
var deptLevelToDeactivate = new List<DeptLevelViewModel>();
// 取得部門層級資料
var uofDeptLevs = await UofxService.BASE.DeptLevel.Get(_corpCode);
// 建立以 Code 為鍵的字典以加快查找速度
var hrDeptDict = hrDeptLevs.ToDictionary(x => x.Code, x => x);
var uofDeptDict = uofDeptLevs.ToDictionary(x => x.Code, x => x);
// 處裡要更新和新增的項目
foreach (var hrDept in hrDeptLevs)
{
// 若此項目已存在,加入更新 List
if (uofDeptDict.ContainsKey(hrDept.Code))
{
deptLevelToUpdate.Add(hrDept);
}
// 此項目不存在,加入新增 List
else
{
deptLevelToAdd.Add(hrDept);
}
};
// 處理要停用的項目
foreach (var uofDept in uofDeptLevs)
{
// 若此項目不存在於 HR 系統,加入停用 List
if (!hrDeptDict.ContainsKey(uofDept.Code))
{
deptLevelToDeactivate.Add(uofDept);
};
};
// 更新部門層級
await UpdateDeptLevels(deptLevelToUpdate);
// 新增部門層級
await CreateDeptLevels(deptLevelToAdd);
// 停用部門層級
await DeactivateDeptLevels(deptLevelToDeactivate);
};
// 更新部門層級
async Task UpdateDeptLevels(List<DeptLevelViewModel> deptLevels)
{
foreach (var deptLevel in deptLevels)
{
try
{
// 更新部門層級狀態
var updateStatus = await UofxService.BASE.DeptLevel.UpdateStatus(new DeptLevelUpdateStatusModel()
{
CorpCode = _corpCode, // 公司代號
Code = deptLevel.Code, // 部門層級代號
Active = true // 是否啟用部門層級
});
// 更新部門層級資料
var update = await UofxService.BASE.DeptLevel.Update(new DeptLevelUpdateModel()
{
CorpCode = _corpCode, // 公司代號
OriginalCode = deptLevel.Code, // 原始部門層級代號 ( 設為部門層級代號 )
Code = deptLevel.Code, // 部門層級代號
Name = deptLevel.Name // 部門層級名稱
});
// 若部門層級狀態、資料更新成功,加入成功訊息
if (updateStatus == true && update == true) successMsg.Add($"已更新 {deptLevel.Code} 部門層級");
}
catch (Exception ex)
{
// 捕捉錯誤並記錄到 errorMsg 中
errorMsg.Add($"無法更新 {deptLevel.Code} 部門層級,error: {ex.Message}");
}
};
};
// 新增部門層級
async Task CreateDeptLevels(List<DeptLevelViewModel> deptLevels)
{
foreach (var deptLevel in deptLevels)
{
try
{
// 新增部門層級
var resualt = await UofxService.BASE.DeptLevel.Create(deptLevel);
// 若新增成功,加入成功訊息
if (resualt == true) successMsg.Add($"已新增 {deptLevel.Code} 部門層級");
}
catch (Exception ex)
{
// 捕捉錯誤並記錄到 errorMsg 中
errorMsg.Add($"無法新增 {deptLevel.Code} 部門層級,error: {ex.Message}");
}
};
};
// 停用部門層級
async Task DeactivateDeptLevels(List<DeptLevelViewModel> deptLevels)
{
foreach (var deptLevel in deptLevels)
{
try
{
// 停用部門層級
var resualt = await UofxService.BASE.DeptLevel.UpdateStatus(new DeptLevelUpdateStatusModel()
{
CorpCode = _corpCode, // 公司代號
Code = deptLevel.Code, // 部門層級代號
Active = false // 停用部門層級
});
// 若停用成功,加入成功訊息
if (resualt == true) successMsg.Add($"已停用 {deptLevel.Code} 部門層級");
}
catch (Exception ex)
{
// 捕捉錯誤並記錄到 errorMsg 中
errorMsg.Add($"無法停用 {deptLevel.Code} 部門層級,error: {ex.Message}");
}
};
};
// 比對與同步部門資料
async Task MapDepts(List<DeptModel> hrDepts)
{
// 更新 List
var deptToUpdate = new List<DeptModel>();
// 新增 List
var deptToAdd = new List<DeptModel>();
// 停用 List
var deptToDeactivate = new List<DeptModel>();
// 取得部門資料
var uofDepts = await UofxService.BASE.Department.Get(new DepartmentGetModel() { CorpCode = _corpCode, IncludeSubDept = true });
// 建立以 Code 為鍵的字典以加快查找速度
var hrDeptDict = hrDepts.ToDictionary(x => x.Code, x => x);
var uofDeptDict = uofDepts.ToDictionary(x => x.Code, x => x);
// 處理要更新和新增的項目
foreach (var hrDept in hrDepts)
{
// 若此項目已存在,加入更新 List
if (uofDeptDict.ContainsKey(hrDept.Code))
{
deptToUpdate.Add(new DeptModel
{
ParentCode = hrDept.ParentCode, // 父部門代號 ( 可設定為空值,代表無父部門 )
Code = hrDept.Code, // 部門代號
Name = hrDept.Name, // 部門名稱
DeptLevelCode = hrDept.DeptLevelCode, // 部門層級代號
Description = hrDept.Description, // 描述
Active = hrDept.Active, // 是否啟用部門
Seq = hrDept.Seq // 部門的排序 ( 從 0 開始 )
});
}
// 此項目不存在,加入新增 List
else
{
deptToAdd.Add(new DeptModel
{
ParentCode = hrDept.ParentCode, // 父部門代號 ( 可設定為空值,代表無父部門 )
Code = hrDept.Code, // 部門代號
Name = hrDept.Name, // 部門名稱
DeptLevelCode = hrDept.DeptLevelCode, // 部門層級代號
Description = hrDept.Description, // 描述
Active = hrDept.Active, // 是否啟用部門
Seq = hrDept.Seq // 部門的排序 ( 從 0 開始 )
});
}
};
// 處理要停用的項目
foreach (var uofDept in uofDepts)
{
// 若此項目不存在於 HR 系統,加入停用 List
if (!hrDeptDict.ContainsKey(uofDept.Code))
{
deptToDeactivate.Add(new DeptModel
{
Code = uofDept.Code, // 部門代號
Active = uofDept.Active, // 是否啟用部門
DeptLevelCode = uofDept.DeptLevelCode // 部門層級代號
});
}
};
// 更新部門
await UpdateDepartments(deptToUpdate);
// 新增部門
await CreateDepartments(deptToAdd);
// 停用部門
await DeactivateDepartments(deptToDeactivate);
};
// 更新部門
async Task UpdateDepartments(List<DeptModel> departments)
{
foreach (var dept in departments)
{
try
{
// 更新部門狀態
var updateStatus = await UofxService.BASE.Department.UpdateState(new DepartmentUpdateStatusModel()
{
CorpCode = _corpCode, // 公司代號
Code = dept.Code, // 部門代號
Active = dept.Active // 是否啟用部門
});
// 更新部門資料
var update = await UofxService.BASE.Department.Update(new DepartmentUpdateModel()
{
CorpCode = _corpCode, // 公司代號
OriginalCode = dept.Code, // 原始部門代號 ( 設為部門代號 )
Code = dept.Code, // 部門代號
Name = dept.Name, // 部門名稱
DeptLevelCode = dept.DeptLevelCode, // 部門層級代號
Description = dept.Description // 描述
});
// 若部門狀態、資料更新成功,加入成功訊息
if (update == true) successMsg.Add($"已更新 {dept.Code} 部門");
}
catch (Exception ex)
{
// 捕捉錯誤並記錄到 errorMsg 中
errorMsg.Add($"無法更新 {dept.Code} 部門,error: {ex.Message}");
}
};
};
// 新增部門
async Task CreateDepartments(List<DeptModel> departments)
{
try
{
foreach (var dept in departments)
{
// 新增部門
var result = await UofxService.BASE.Department.Create(new DepartmentCreateModel()
{
ParentCode = dept.ParentCode, // 父部門代號 ( 可設定為空值,代表無父部門 )
CorpCode = _corpCode, // 公司代號
Code = dept.Code, // 部門代號
DeptLevelCode = dept.DeptLevelCode, // 部門層級代號
Name = dept.Name, // 部門名稱
Description = dept.Description // 描述
});
// 若新增成功,加入成功訊息
if (result == true) successMsg.Add($"已新增 {dept.Code} 部門");
};
}
catch (Exception ex)
{
// 捕捉錯誤並記錄到 errorMsg 中
errorMsg.Add($"無法新增 {departments} 部門,error: {ex.Message}");
}
};
// 停用部門
async Task DeactivateDepartments(List<DeptModel> departments)
{
foreach (var dept in departments)
{
try
{
// 停用部門
var result = await UofxService.BASE.Department.UpdateState(new DepartmentUpdateStatusModel()
{
CorpCode = _corpCode, // 公司代號
Code = dept.Code, // 部門代號
Active = false // 是否啟用部門
});
// 若停用成功,加入成功訊息
if (result == true) successMsg.Add($"已停用 {dept.Code} 部門");
}
catch (Exception ex)
{
// 捕捉錯誤並記錄到 errorMsg 中
errorMsg.Add($"無法停用 {dept.Code} 部門,error: {ex.Message}");
}
};
};
#endregion
#region 同步人員
// 同步人員資料
async Task SyncEmpls(List<HrEmplModel> originHrEmpls)
{
// 同步職稱職務
await SyncJobTitlesAndFunctions(originHrEmpls);
// 轉換人員 List
var hrEmpls = ConvertToEmplModels(originHrEmpls);
// 比對與同步人員資料
await MapEmpls(hrEmpls);
};
// 同步職稱職務
async Task SyncJobTitlesAndFunctions(List<HrEmplModel> originHrEmpls)
{
// 取得 HR 系統職稱職務
List<string> JobTitleCodes = originHrEmpls.Select(e => e.JobTitleID).ToList();
List<string> JobFuncs = originHrEmpls.SelectMany(e => e.JobFunctions ?? new List<string>()).ToList();
// 取得 UOF X 系統職稱職務
var uofJobTitles = await UofxService.BASE.JobTitle.Get(_corpCode);
var uofJobFuncs = await UofxService.BASE.JobFunc.Get(_corpCode);
// 找出要新增的職稱職務
var JobTitleToAdd = JobTitleCodes.Except(uofJobTitles.Select(j => j.Code)).ToList();
var JobFuncToAdd = JobFuncs.Except(uofJobFuncs.Select(j => j.Code)).ToList();
foreach (var jobTitle in JobTitleToAdd)
{
try
{
// 新增職稱
var result = await UofxService.BASE.JobTitle.Create(new JobTitleViewModel()
{
CorpCode = _corpCode, // 職稱代號
Rank = 50, // 職稱階層 ( 將簽核層級預設為 50 再由管理員到 UOF X 調整 )
Seq = 1, // 職稱排序 ( 從 1 開始 )
Code = jobTitle, // 職稱代號
Title = jobTitle, // 職稱名稱
Active = true // 啟用職稱
});
// 若新增成功,加入成功訊息
if (result == true) successMsg.Add($"已新增 {jobTitle} 職稱");
}
catch (Exception ex)
{
// 捕捉錯誤並記錄到 errorMsg 中
errorMsg.Add($"無法新增 {jobTitle} 職稱,error: {ex.Message}");
}
};
foreach (var jobFunc in JobFuncToAdd)
{
try
{
// 新增職務
var result = await UofxService.BASE.JobFunc.Create(new JobFuncViewModel()
{
CorpCode = _corpCode, // 職稱代號
CategoryName = jobFunc, // 職務類別名稱
Code = jobFunc, // 職務代號
JobFunc = jobFunc, // 職務名稱
Active = true // 啟用職務
});
// 若新增成功,加入成功訊息
if (result == true) successMsg.Add($"已新增 {jobFunc} 職務");
}
catch (Exception ex)
{
// 捕捉錯誤並記錄到 errorMsg 中
errorMsg.Add($"無法新增 {jobFunc} 職務,error: {ex.Message}");
}
};
};
// 轉換人員 List
List<EmplModel> ConvertToEmplModels(List<HrEmplModel> originHrEmpls)
{
// 以 Account 作為 key 分組
var groupedEmpls = originHrEmpls.GroupBy(e => e.Account);
// 每一組的分組資料 g 轉換成一個新的 EmplModel
return groupedEmpls.Select(g => new EmplModel
{
CorpCode = _corpCode, // 公司代號
Account = g.Key, // 人員帳號
Name = g.First().Name, // 中文姓名
Gender = g.First().Gender == "male" ? "0" : (g.First().Gender == "female" ? "1" : "2"), // 性別 ( 男性 male => 0, 女性 female => 1, 其他 other => 2 )
EnglishName = g.First().EnglishName, // 英文姓名
EmployeeNumber = g.First().EmployeeID, // 員工編號
ExpiredTime = g.First().ExpiredTime, // 帳號過期時間
ResignationDate = g.First().ResignationDate, // 離職日
IdCardNumber = g.First().IDNumber, // 身份證字號
BirthDate = new DateTime(g.First().BirthDate.Year + 1911, g.First().BirthDate.Month, g.First().BirthDate.Day), // 生日 (民國年轉為西元年)
PhoneNumber = g.First().PhoneNumber, // 行動電話
BusinessCard = g.First().CardTitle, // 名片職稱
HireDate = g.First().HireDate, // 到職日
Email = g.First().PrimaryEmail, // 主要信箱
EmailEx = g.First().SecondaryEmail, // 其他信箱
Active = g.First().AccountActive, // 是否停用人員帳號
Locked = g.First().AccountLocked, // 是否鎖定人員帳號
// 將每個分組中的資料轉換成一個新的 EmpCreateOfDeptItemRequestModel
Depts = g.Select(e => new EmpCreateOfDeptItemRequestModel
{
Code = e.DeptCode, // 部門代號
IsMainDept = e.PrimaryDepartment, // 是否為主要部門
JobTitleCode = e.JobTitleID, // 職稱代號
JobFuncs = e.JobFunctions // 職務代號 ( 可多筆 )
}).ToList(),
// 將每個分組中的主管資料轉換成一個新的 DeptSetManagerModel
Supervisor = g.Where(g => g.IsSupervisor).Select(e => new DeptSetManagerModel
{
CorpCode = _corpCode, // 公司代號
Code = e.DeptCode, // 部門代號
Type = UserType.Account, // 人員的類別 ( 設為 Account )
Value = e.Account // 帳號
}).ToList()
}).ToList();
};
// 比對與同步人員資料
async Task MapEmpls(List<EmplModel> hrEmpls)
{
// 更新 List
var emplToUpdate = new List<EmplModel>();
// 新增 List
var emplToAdd = new List<EmplModel>();
// 停用 List
var emplToDeactivate = new List<EmplModel>();
// 將已存在人員放入需更新 List,不存在人員放入需新增 List
foreach (var hrEmpl in hrEmpls)
{
// 取得人員資料
var empl = await UofxService.BASE.OrgEmpl.Get(new EmplQueryRequestModel()
{
CorpCode = _corpCode, // 公司代號
UserType = UserType.Account, // 人員的類別 ( 設為 Account )
UserCode = hrEmpl.Account // 帳號
});
// 若人員存在,加入更新 List
if (empl.Account != null)
{
emplToUpdate.Add(hrEmpl);
}
// 不存在,加入新增 List
else
{
emplToAdd.Add(hrEmpl);
// 如果現在日期大於帳號過期時間,加入停用 List
if (hrEmpl.ExpiredTime != null && DateTime.Now > hrEmpl.ExpiredTime)
{
emplToDeactivate.Add(hrEmpl);
};
}
};
// 更新人員
await UpdateEmpls(emplToUpdate);
// 新增人員
await CreateEmpls(emplToAdd);
// 停用人員
await DeactivateEmpls(emplToDeactivate);
};
// 更新人員
async Task UpdateEmpls(List<EmplModel> empls)
{
foreach (var empl in empls)
{
try
{
// 更新人員狀態
var updateStatus = await UofxService.BASE.OrgEmpl.UpdateAcctStatus(new EmplUpdateAcctStatusRequestModel
{
CorpCode = _corpCode, // 公司代號
UserType = UserType.Account, // 人員的類別 ( 設為 Account )
UserCode = empl.Account, // 帳號
Active = empl.Active // 是否停用人員帳號
});
// 更新人員鎖定狀態
var updateLocked = await UofxService.BASE.OrgEmpl.UpdateAcctLocked(new EmplUpdateAcctLockedRequestModel
{
CorpCode = _corpCode, // 公司代號
UserType = UserType.Account, // 人員的類別 ( 設為 Account )
UserCode = empl.Account, // 帳號
Locked = empl.Locked // 是否鎖定人員帳號
});
// 更新人員資料
var update = await UofxService.BASE.OrgEmpl.Update(new EmplUpdateRequestModel
{
CorpCode = _corpCode, // 公司代號
UserType = UserType.Account, // 人員的類別 ( 設為 Account )
UserCode = empl.Account, // 帳號
Account = empl.Account, // 人員帳號
Name = empl.Name, // 中文姓名
EmployeeNumber = empl.EmployeeNumber, // 員工編號
Gender = empl.Gender, // 性別
EnglishName = empl.EnglishName, // 英文姓名
IdCardNumber = empl.IdCardNumber, // 身份證字號
BirthDate = empl.BirthDate, // 生日
PhoneNumber = empl.PhoneNumber, // 行動電話
BusinessCard = empl.BusinessCard, // 名片職稱
HireDate = empl.HireDate, // 到職日
Email = empl.Email, // 主要信箱
EmailEx = empl.EmailEx // 其他信箱
});
// 更新人員過期時間
var updateExpiredTime = await UofxService.BASE.OrgEmpl.UpdateAcctExpiredTime(new EmplUpdateAcctExpiredTimeRequestModel
{
CorpCode = _corpCode, // 公司代號
UserType = UserType.Account, // 人員的類別 ( 設為 Account )
UserCode = empl.Account, // 帳號
ExpiredTime = empl.ExpiredTime // 帳號過期時間
});
// 更新人員離職日期
var updateResignationDate = await UofxService.BASE.OrgEmpl.UpdateEmplResignationDate(new EmplUpdateResignationDateRequestModel
{
CorpCode = _corpCode, // 公司代號
UserType = UserType.Account, // 人員的類別 ( 設為 Account )
UserCode = empl.Account, // 帳號
ResignationDate = empl.ResignationDate // 離職日
});
// 更新人員部門
var updateDepts = await UofxService.BASE.OrgEmpl.UpdateEmplDept(new EmplUpdateDeptRequestModel
{
CorpCode = _corpCode, // 公司代號
UserType = UserType.Account, // 人員的類別 ( 設為 Account )
UserCode = empl.Account, // 帳號
Depts = empl.Depts.Select(d => new DeptRequestModel
{
Code = d.Code, // 部門代號
IsMainDept = d.IsMainDept, // 是否為主要部門
JobTitleCode = d.JobTitleCode, // 職稱代號
JobFuncs = d.JobFuncs // 職務代號 ( 可多筆 )
}).ToList()
});
// 若有部門主管,同步部門主管
if (empl.Supervisor != null && empl.Supervisor.Count > 0)
{
await SyncSuperiors(empl.Supervisor);
};
// 若所有更新皆成功,記錄成功訊息
if (updateStatus == true && updateLocked == true && update == true && updateExpiredTime == true && updateResignationDate == true && updateDepts == true)
{
successMsg.Add($"已更新 {empl.Account} 人員");
};
}
catch (Exception ex)
{
// 捕捉錯誤並記錄到 errorMsg 中
errorMsg.Add($"無法更新 {empl.Account} 人員,error: {ex.Message}");
}
};
};
// 新增人員
async Task CreateEmpls(List<EmplModel> empls)
{
foreach (var empl in empls)
{
try
{
// 新增人員
var result = await UofxService.BASE.OrgEmpl.CreateEmpl(new EmpCreateRequestModel
{
CorpCode = _corpCode, // 公司代號
Account = empl.Account, // 人員帳號
Name = empl.Name, // 中文姓名
EnglishName = empl.EnglishName, // 英文姓名
EmployeeNumber = empl.EmployeeNumber, // 員工編號
ExpiredTime = empl.ExpiredTime, // 帳號過期時間
Gender = empl.Gender, // 性別
IdCardNumber = empl.IdCardNumber, // 身份證字號
BirthDate = empl.BirthDate, // 生日
PhoneNumber = empl.PhoneNumber, // 行動電話
BusinessCard = empl.BusinessCard, // 名片職稱
HireDate = empl.HireDate, // 到職日
Email = empl.Email, // 主要信箱
EmailEx = empl.EmailEx, // 其他信箱
Depts = empl.Depts
});
// 若新增成功,加入成功訊息
if (result == true)
{
successMsg.Add($"已新增 {empl.Account} 人員");
// 若有部門主管,同步部門主管
if (empl.Supervisor != null && empl.Supervisor.Count > 0)
{
await SyncSuperiors(empl.Supervisor);
};
};
}
catch (Exception ex)
{
// 捕捉錯誤並記錄到 errorMsg 中
errorMsg.Add($"無法新增 {empl.Account} 人員,error: {ex.Message}");
}
};
};
// 停用人員
async Task DeactivateEmpls(List<EmplModel> empls)
{
foreach (var empl in empls)
{
try
{
// 停用人員
var result = await UofxService.BASE.OrgEmpl.UpdateAcctStatus(new EmplUpdateAcctStatusRequestModel
{
CorpCode = _corpCode, // 公司代號
UserType = UserType.Account, // 人員的類別 ( 設為 Account )
UserCode = empl.Account, // 帳號
Active = false // 停用人員帳號
});
// 若停用成功,加入成功訊息
if (result == true) successMsg.Add($"已停用 {empl.Account} 人員");
}
catch (Exception ex)
{
// 捕捉錯誤並記錄到 errorMsg 中
errorMsg.Add($"無法停用 {empl.Account} 人員,error: {ex.Message}");
}
};
};
// 同步部門主管
async Task SyncSuperiors(List<DeptSetManagerModel> Superiors)
{
foreach (var Superior in Superiors)
{
try
{
// 設定部門主管
var result = await UofxService.BASE.Department.Manager.Set(Superior);
// 若設定成功,加入成功訊息
if (result == true) successMsg.Add($"已設定 {Superior.Value} 為 {Superior.Code} 部門主管");
}
catch (Exception ex)
{
// 捕捉錯誤並記錄到 errorMsg 中
errorMsg.Add($"無法設定 {Superior.Value} 為 {Superior.Code} 部門主管,error: {ex.Message}");
}
};
};
#endregion
# region 類別
/// <summary>
/// UOF X 部門類別
/// </summary>
public class DeptModel
{
public string Code { get; set; }
public string Name { get; set; }
public bool Active { get; set; }
public bool IncludeSubDept { get; set; }
public string ParentCode { get; set; }
public string Description { get; set; }
public string DeptLevelCode { get; set; }
public int Seq { get; set; }
public List<DeptModel> SubDepts { get; set; } = new List<DeptModel>(); // 用於儲存子部門
}
/// <summary>
/// UOF X 人員類別
/// </summary>
public class EmplModel
{
public string CorpCode { get; set; }
public string Account { get; set; }
public string Name { get; set; }
public string EnglishName { get; set; }
public string EmployeeNumber { get; set; }
public DateTime? ExpiredTime { get; set; }
public DateTime? ResignationDate { get; set; }
public string Gender { get; set; }
public string IdCardNumber { get; set; }
public DateTime? BirthDate { get; set; }
public string PhoneNumber { get; set; }
public string BusinessCard { get; set; }
public DateTime? HireDate { get; set; }
public string Email { get; set; }
public string EmailEx { get; set; }
public bool Locked { get; set; }
public bool Active { get; set; }
public bool IsSupervisor { get; set; }
public List<EmpCreateOfDeptItemRequestModel> Depts { get; set; }
public List<DeptSetManagerModel> Supervisor { get; set; }
}
/// <summary>
/// HR 部門類別
/// </summary>
public class HrDeptModel
{
public string DeptName { get; set; }
public string DeptCode { get; set; }
public int Order { get; set; }
public bool IsActive { get; set; }
public bool HasSubDepartments { get; set; }
public string ParentDeptCode { get; set; }
public string DeptDescription { get; set; }
public string DeptLevelCode { get; set; }
public List<HrDeptModel> SubDepts { get; set; } = new List<HrDeptModel>();
}
/// <summary>
/// HR 人員類別
/// </summary>
public class HrEmplModel
{
public string Account { get; set; }
public string Name { get; set; }
public string EnglishName { get; set; }
public string EmployeeID { get; set; }
public DateTime? ExpiredTime { get; set; }
public string Gender { get; set; }
public string IDNumber { get; set; }
public DateTime BirthDate { get; set; }
public string PhoneNumber { get; set; }
public string CardTitle { get; set; }
public DateTime HireDate { get; set; }
public DateTime? ResignationDate { get; set; }
public string PrimaryEmail { get; set; }
public string SecondaryEmail { get; set; }
public bool AccountLocked { get; set; }
public bool AccountActive { get; set; }
public string JobTitleID { get; set; }
public List<string> JobFunctions { get; set; }
public string DeptCode { get; set; }
public bool PrimaryDepartment { get; set; }
public bool IsSupervisor { get; set; }
}
#endregion
- 因為資料量較龐大,為了不影響觀看範例,請自行將 HR 系統資料 複製貼上到下方的
originHrDepts
與originHrEmpls
中
HR 系統資料¶
HR 系統資料
// HR 系統部門資料
List<HrDeptModel> originHrDepts = new List<HrDeptModel>
{
new HrDeptModel
{
DeptName = "Research and Development",
DeptCode = "RD",
Order = 2,
IsActive = true,
HasSubDepartments = false,
ParentDeptCode = "HR",
DeptDescription = "Contamination NEC"
},
new HrDeptModel
{
DeptName = "Business Development",
DeptCode = "BD",
Order = 1,
IsActive = true,
HasSubDepartments = false,
ParentDeptCode = "HR",
DeptDescription = "Adenovirus infect NOS"
},
new HrDeptModel
{
DeptName = "Training",
DeptCode = "T",
Order = 3,
IsActive = true,
HasSubDepartments = false,
ParentDeptCode = "HR",
DeptDescription = "TB peritonitis-histo dx"
},
new HrDeptModel
{
DeptName = "Human Resources",
DeptCode = "HR",
Order = 1,
IsActive = true,
HasSubDepartments = true,
ParentDeptCode = "S",
DeptDescription = "Spon abor w pel inf-unsp"
},
new HrDeptModel
{
DeptName = "Services",
DeptCode = "S",
Order = 1,
IsActive = true,
HasSubDepartments = true,
ParentDeptCode = "",
DeptDescription = "Oth coll stn obj-per NEC"
}
};
// HR 系統人員資料
List<HrEmplModel> originHrEmpls = new List<HrEmplModel>
{
new HrEmplModel{
Account = "costanza",
Name = "松源",
EnglishName = "costanza Posvner",
EmployeeID = "A3897",
ExpiredTime = null,
Gender = "female",
IDNumber = "A123456789",
BirthDate = new DateTime(85, 7, 25),
PhoneNumber = "0946323278",
CardTitle = "Human Resources Assistant II",
HireDate = new DateTime(2013, 12, 21),
ResignationDate = new DateTime(2027, 10, 18),
PrimaryEmail = "cposvner0@jalbum.net",
SecondaryEmail = "cposvner0@phoca.cz",
AccountLocked = false,
AccountActive = true,
JobTitleID = "UX",
JobFunctions = null,
DeptCode = "T",
PrimaryDepartment = true,
IsSupervisor = true
},
new HrEmplModel{
Account = "natty",
Name = "丰逸",
EnglishName = "natty Yankin",
EmployeeID = "A9618",
ExpiredTime = null,
Gender = "female",
IDNumber = "A123456789",
BirthDate = new DateTime(85, 1, 14),
PhoneNumber = "0934756998",
CardTitle = "Pharmacist",
HireDate = new DateTime(2020, 3, 2),
ResignationDate = null,
PrimaryEmail = "nyankin1@wiley.com",
SecondaryEmail = null,
AccountLocked = false,
AccountActive = true,
JobTitleID = "PM",
JobFunctions = new List<string> { "資訊安全管理" },
DeptCode = "HR",
PrimaryDepartment = true,
IsSupervisor = false
},
new HrEmplModel{
Account = "julina",
Name = "思宇",
EnglishName = "julina Andryushchenko",
EmployeeID = "A8329",
ExpiredTime = new DateTime(2029, 2, 10),
Gender = "other",
IDNumber = "A123456789",
BirthDate = new DateTime(87, 4, 10),
PhoneNumber = "0960836336",
CardTitle = "Budget/Accounting Analyst III",
HireDate = new DateTime(2024, 2, 7),
ResignationDate = null,
PrimaryEmail = "jandryushchenko2@amazon.co.uk",
SecondaryEmail = null,
AccountLocked = false,
AccountActive = true,
JobTitleID = "TA",
JobFunctions = null,
DeptCode = "RD",
PrimaryDepartment = true,
IsSupervisor = false
},
new HrEmplModel{
Account = "catharina",
Name = "琪煜",
EnglishName = "catharina Edmonson",
EmployeeID = "A6879",
ExpiredTime = null,
Gender = "female",
IDNumber = "A123456789",
BirthDate = new DateTime(90, 7, 18),
PhoneNumber = "0995946581",
CardTitle = "Actuary",
HireDate = new DateTime(2012, 4, 15),
ResignationDate = null,
PrimaryEmail = "cedmonson3@free.fr",
SecondaryEmail = null,
AccountLocked = false,
AccountActive = true,
JobTitleID = "TA",
JobFunctions = null,
DeptCode = "BD",
PrimaryDepartment = true,
IsSupervisor = false
},
new HrEmplModel{
Account = "nita",
Name = "梓焓",
EnglishName = "nita Lowre",
EmployeeID = "A4523",
ExpiredTime = new DateTime(2025, 9, 2),
Gender = "male",
IDNumber = "A123456789",
BirthDate = new DateTime(63, 7, 18),
PhoneNumber = "0995537127",
CardTitle = "Compensation Analyst",
HireDate = new DateTime(2011, 5, 17),
ResignationDate = null,
PrimaryEmail = "nlowre4@privacy.gov.au",
SecondaryEmail = null,
AccountLocked = false,
AccountActive = true,
JobTitleID = "SA",
JobFunctions = new List<string> { "使用者介面設計" },
DeptCode = "T",
PrimaryDepartment = true,
IsSupervisor = false
},
new HrEmplModel{
Account = "cary",
Name = "可馨",
EnglishName = "cary Ralton",
EmployeeID = "A4550",
ExpiredTime = null,
Gender = "male",
IDNumber = "A123456789",
BirthDate = new DateTime(62, 1, 26),
PhoneNumber = "0969873340",
CardTitle = "Dental Hygienist",
HireDate = new DateTime(2008, 11, 24),
ResignationDate = null,
PrimaryEmail = "cralton5@admin.ch",
SecondaryEmail = null,
AccountLocked = false,
AccountActive = true,
JobTitleID = "QA",
JobFunctions = null,
DeptCode = "BD",
PrimaryDepartment = true,
IsSupervisor = false
},
new HrEmplModel{
Account = "gelya",
Name = "培安",
EnglishName = "gelya Louder",
EmployeeID = "A8117",
ExpiredTime = null,
Gender = "male",
IDNumber = "A123456789",
BirthDate = new DateTime(74, 3, 20),
PhoneNumber = "0971386307",
CardTitle = "Compensation Analyst",
HireDate = new DateTime(2013, 7, 16),
ResignationDate = null,
PrimaryEmail = "glouder6@homestead.com",
SecondaryEmail = null,
AccountLocked = false,
AccountActive = true,
JobTitleID = "PM",
JobFunctions = null,
DeptCode = "HR",
PrimaryDepartment = true,
IsSupervisor = false
},
new HrEmplModel
{
Account = "nita",
Name = "梓焓",
EnglishName = "nita Lowre",
EmployeeID = "A4523",
ExpiredTime = new DateTime(2025, 9, 2),
Gender = "male",
IDNumber = "A123456789",
BirthDate = new DateTime(63, 7, 18),
PhoneNumber = "0995537127",
CardTitle = "Compensation Analyst",
HireDate = new DateTime(2011, 5, 17),
ResignationDate = null,
PrimaryEmail = "nlowre4@privacy.gov.au",
SecondaryEmail = null,
AccountLocked = false,
AccountActive = true,
JobTitleID = "TA",
JobFunctions = null,
DeptCode = "HR",
PrimaryDepartment = false,
IsSupervisor = false
},
new HrEmplModel
{
Account = "costanza",
Name = "松源",
EnglishName = "costanza Posvner",
EmployeeID = "A3897",
ExpiredTime = null,
Gender = "female",
IDNumber = "A123456789",
BirthDate = new DateTime(85, 7, 25),
PhoneNumber = "0946323278",
CardTitle = "Human Resources Assistant II",
HireDate = new DateTime(2013, 12, 21),
ResignationDate = new DateTime(2027, 10, 18),
PrimaryEmail = "cposvner0@jalbum.net",
SecondaryEmail = "cposvner0@phoca.cz",
AccountLocked = false,
AccountActive = true,
JobTitleID = "UX",
JobFunctions = new List<string> { "資訊安全管理" },
DeptCode = "RD",
PrimaryDepartment = false,
IsSupervisor = false
},
new HrEmplModel
{
Account = "carmelina",
Name = "漫妮",
EnglishName = "carmelina Beadle",
EmployeeID = "A3583",
ExpiredTime = null,
Gender = "male",
IDNumber = "A123456789",
BirthDate = new DateTime(85, 10, 8),
PhoneNumber = "0911960804",
CardTitle = "Accounting Assistant I",
HireDate = new DateTime(2011, 3, 26),
ResignationDate = new DateTime(2027, 9, 25),
PrimaryEmail = "cbeadle9@go.com",
SecondaryEmail = null,
AccountLocked = false,
AccountActive = true,
JobTitleID = "TA",
JobFunctions = new List<string> { "軟體開發" },
DeptCode = "HR",
PrimaryDepartment = true,
IsSupervisor = false
},
new HrEmplModel
{
Account = "raviv",
Name = "澤瀚",
EnglishName = "raviv Atcherley",
EmployeeID = "A8007",
ExpiredTime = null,
Gender = "male",
IDNumber = "A123456789",
BirthDate = new DateTime(98, 4, 20),
PhoneNumber = "0976843491",
CardTitle = "Tax Accountant",
HireDate = new DateTime(2018, 4, 21),
ResignationDate = null,
PrimaryEmail = "ratcherleya@yellowpages.com",
SecondaryEmail = null,
AccountLocked = false,
AccountActive = true,
JobTitleID = "TA",
JobFunctions = null,
DeptCode = "HR",
PrimaryDepartment = true,
IsSupervisor = false
},
new HrEmplModel
{
Account = "chev",
Name = "雅靜",
EnglishName = "chev Rowell",
EmployeeID = "A287",
ExpiredTime = null,
Gender = "female",
IDNumber = "A123456789",
BirthDate = new DateTime(88, 5, 2),
PhoneNumber = "0913313487",
CardTitle = "Automation Specialist I",
HireDate = new DateTime(2011, 5, 4),
ResignationDate = null,
PrimaryEmail = "crowellb@paginegialle.it",
SecondaryEmail = null,
AccountLocked = false,
AccountActive = true,
JobTitleID = "BA",
JobFunctions = null,
DeptCode = "S",
PrimaryDepartment = true,
IsSupervisor = true
},
new HrEmplModel
{
Account = "ezmeralda",
Name = "若瑾",
EnglishName = "ezmeralda Durkin",
EmployeeID = "A6996",
ExpiredTime = new DateTime(2029, 5, 18),
Gender = "other",
IDNumber = "A123456789",
BirthDate = new DateTime(56, 6, 8),
PhoneNumber = "0918733181",
CardTitle = "Office Assistant I",
HireDate = new DateTime(2024, 10, 28),
ResignationDate = new DateTime(2027, 1, 9),
PrimaryEmail = "edurkinc@tripadvisor.com",
SecondaryEmail = null,
AccountLocked = false,
AccountActive = true,
JobTitleID = "UX",
JobFunctions = null,
DeptCode = "RD",
PrimaryDepartment = true,
IsSupervisor = true
},
new HrEmplModel
{
Account = "rina",
Name = "俊澤",
EnglishName = "rina Cootes",
EmployeeID = "A9043",
ExpiredTime = new DateTime(2029, 10, 8),
Gender = "female",
IDNumber = "A123456789",
BirthDate = new DateTime(62, 6, 6),
PhoneNumber = "0971141652",
CardTitle = "Senior Quality Engineer",
HireDate = new DateTime(2017, 3, 9),
ResignationDate = null,
PrimaryEmail = "rcootesd@unblog.fr",
SecondaryEmail = null,
AccountLocked = false,
AccountActive = true,
JobTitleID = "UX",
JobFunctions = new List<string> { "軟體開發" },
DeptCode = "HR",
PrimaryDepartment = true,
IsSupervisor = false
},
new HrEmplModel
{
Account = "dory",
Name = "雨婷",
EnglishName = "dory Ashard",
EmployeeID = "A6241",
ExpiredTime = null,
Gender = "male",
IDNumber = "A123456789",
BirthDate = new DateTime(61, 4, 4),
PhoneNumber = "0944897131",
CardTitle = "Business Systems Development Analyst",
HireDate = new DateTime(2013, 2, 25),
ResignationDate = null,
PrimaryEmail = "dasharde@latimes.com",
SecondaryEmail = null,
AccountLocked = false,
AccountActive = true,
JobTitleID = "BE",
JobFunctions = null,
DeptCode = "HR",
PrimaryDepartment = true,
IsSupervisor = true
},
new HrEmplModel
{
Account = "conway",
Name = "浩霖",
EnglishName = "conway McAlpine",
EmployeeID = "A2169",
ExpiredTime = null,
Gender = "female",
IDNumber = "A123456789",
BirthDate = new DateTime(100, 6, 25),
PhoneNumber = "0975728820",
CardTitle = "Physical Therapy Assistant",
HireDate = new DateTime(2016, 4, 8),
ResignationDate = new DateTime(2029, 12, 20),
PrimaryEmail = "cmcalpinef@ted.com",
SecondaryEmail = "cmcalpinef@scientificamerican.com",
AccountLocked = false,
AccountActive = true,
JobTitleID = "PM",
JobFunctions = null,
DeptCode = "BD",
PrimaryDepartment = true,
IsSupervisor = false
},
new HrEmplModel
{
Account = "rebekkah",
Name = "博豪",
EnglishName = "rebekkah Trotter",
EmployeeID = "A1287",
ExpiredTime = null,
Gender = "male",
IDNumber = "A123456789",
BirthDate = new DateTime(85, 4, 1),
PhoneNumber = "0981131325",
CardTitle = "Assistant Professor",
HireDate = new DateTime(2020, 5, 8),
ResignationDate = new DateTime(2026, 6, 4),
PrimaryEmail = "rtrotterg@abc.net.au",
SecondaryEmail = null,
AccountLocked = false,
AccountActive = true,
JobTitleID = "IS",
JobFunctions = null,
DeptCode = "T",
PrimaryDepartment = true,
IsSupervisor = false
},
new HrEmplModel
{
Account = "kath",
Name = "博裕",
EnglishName = "kath Duplain",
EmployeeID = "A1665",
ExpiredTime = null,
Gender = "male",
IDNumber = "A123456789",
BirthDate = new DateTime(81, 11, 11),
PhoneNumber = "0934443938",
CardTitle = "Payment Adjustment Coordinator",
HireDate = new DateTime(2011, 12, 21),
ResignationDate = new DateTime(2026, 4, 5),
PrimaryEmail = "kduplainh@cnet.com",
SecondaryEmail = null,
AccountLocked = false,
AccountActive = true,
JobTitleID = "FE",
JobFunctions = null,
DeptCode = "BD",
PrimaryDepartment = true,
IsSupervisor = true
},
new HrEmplModel
{
Account = "betteanne",
Name = "芮涵",
EnglishName = "betteanne Facchini",
EmployeeID = "A3542",
ExpiredTime = null,
Gender = "female",
IDNumber = "A123456789",
BirthDate = new DateTime(62, 6, 8),
PhoneNumber = "0960426450",
CardTitle = "Account Coordinator",
HireDate = new DateTime(2009, 9, 25),
ResignationDate = new DateTime(2029, 11, 13),
PrimaryEmail = "bfacchinii@cnbc.com",
SecondaryEmail = null,
AccountLocked = false,
AccountActive = true,
JobTitleID = "SA",
JobFunctions = null,
DeptCode = "RD",
PrimaryDepartment = true,
IsSupervisor = false
},
new HrEmplModel
{
Account = "betteanne",
Name = "芮涵",
EnglishName = "betteanne Facchini",
EmployeeID = "A3542",
ExpiredTime = null,
Gender = "female",
IDNumber = "A123456789",
BirthDate = new DateTime(62, 6, 8),
PhoneNumber = "0960426450",
CardTitle = "Account Coordinator",
HireDate = new DateTime(2009, 9, 25),
ResignationDate = new DateTime(2029, 11, 13),
PrimaryEmail = "bfacchinii@cnbc.com",
SecondaryEmail = null,
AccountLocked = false,
AccountActive = true,
JobTitleID = "TA",
JobFunctions = new List<string> { "技術支援" },
DeptCode = "T",
PrimaryDepartment = false,
IsSupervisor = false
},
new HrEmplModel
{
Account = "antons",
Name = "辰華",
EnglishName = "antons Walcher",
EmployeeID = "A8962",
ExpiredTime = new DateTime(2029, 5, 14),
Gender = "female",
IDNumber = "A123456789",
BirthDate = new DateTime(79, 7, 26),
PhoneNumber = "0931240619",
CardTitle = "Environmental Tech",
HireDate = new DateTime(2013, 5, 8),
ResignationDate = null,
PrimaryEmail = "awalcherj@google.fr",
SecondaryEmail = null,
AccountLocked = false,
AccountActive = true,
JobTitleID = "BE",
JobFunctions = new List<string> { "後端開發", "技術支援" },
DeptCode = "T",
PrimaryDepartment = true,
IsSupervisor = false
},
new HrEmplModel
{
Account = "antons",
Name = "辰華",
EnglishName = "antons Walcher",
EmployeeID = "A8962",
ExpiredTime = new DateTime(2029, 5, 14),
Gender = "female",
IDNumber = "A123456789",
BirthDate = new DateTime(79, 7, 26),
PhoneNumber = "0931240619",
CardTitle = "Environmental Tech",
HireDate = new DateTime(2013, 5, 8),
ResignationDate = null,
PrimaryEmail = "awalcherj@google.fr",
SecondaryEmail = null,
AccountLocked = false,
AccountActive = true,
JobTitleID = "PM",
JobFunctions = new List<string> { "專案管理" },
DeptCode = "S",
PrimaryDepartment = false,
IsSupervisor = false
}
};
一、同步部門¶
接下來,我們將逐步撰寫範例,首先會針對 HR 系統資料做處理,再將 HR 系統與 UOF X 部門資料進行比對與同步:
A. 部門資料階層化¶
因為該範例的 HR 系統資料並沒有 部門階層,所以在第一步,我們先將部門資料階層化:
- 篩選出
originHrDepts
中HasSubDepartments
為true
的父部門,並儲存到getHRParentDeptList
。 - 針對每個父部門,在
originHrDepts
中篩選出ParentDeptCode
等於父部門代號的子部門,並將結果存入父部門的SubDepts
中。 - 篩選出
ParentDeptCode
為空的部門,表示這些是最高階層的部門,並回傳結果。
如此一來,程式會生成部門的階層結構,每個部門的 SubDepts
欄位會包含其子部門,最上層的部門結構則會作為結果回傳。
List<HrDeptModel> BuildHierarchy(List<HrDeptModel> originHrDepts)
{
// 取得有子部門的父部門資料
var getHRParentDeptList = originHrDepts.Where(x => x.HasSubDepartments).ToList();
// 將子部門資料加入父部門
foreach (var dept in getHRParentDeptList)
{
var getSubDept = originHrDepts.Where(x => x.ParentDeptCode == dept.DeptCode).ToList();
dept.SubDepts = getSubDept;
};
// 取得沒有父部門的部門資料
var hierarchyDeptList = originHrDepts.Where(x => string.IsNullOrEmpty(x.ParentDeptCode)).ToList();
return hierarchyDeptList;
};
資料的變化
new List<HrDeptModel>
{
new HrDeptModel
{
DeptName = "Human Resources",
DeptCode = "HR",
Order = 1,
IsActive = true,
HasSubDepartments = true,
ParentDeptCode = "S",
DeptDescription = "Spon abor w pel inf-unsp"
},
new HrDeptModel
{
DeptName = "Services",
DeptCode = "S",
Order = 1,
IsActive = true,
HasSubDepartments = true,
ParentDeptCode = "",
DeptDescription = "Oth coll stn obj-per NEC"
}
}
new List<HrDeptModel>
{
new HrDeptModel
{
DeptName = "Research and Development",
DeptCode = "RD",
Order = 2,
IsActive = true,
HasSubDepartments = false,
ParentDeptCode = "HR",
DeptDescription = "Contamination NEC"
},
new HrDeptModel
{
DeptName = "Business Development",
DeptCode = "BD",
Order = 1,
IsActive = true,
HasSubDepartments = false,
ParentDeptCode = "HR",
DeptDescription = "Adenovirus infect NOS"
},
new HrDeptModel
{
DeptName = "Training",
DeptCode = "T",
Order = 3,
IsActive = true,
HasSubDepartments = false,
ParentDeptCode = "HR",
DeptDescription = "TB peritonitis-histo dx"
},
new HrDeptModel
{
DeptName = "Human Resources",
DeptCode = "HR",
Order = 1,
IsActive = true,
HasSubDepartments = true,
ParentDeptCode = "S",
DeptDescription = "Spon abor w pel inf-unsp"
SubDepts = new List<HrDeptModel>
{
new HrDeptModel
{
DeptName = "Research and Development",
DeptCode = "RD",
Order = 2,
IsActive = true,
HasSubDepartments = false,
ParentDeptCode = "HR",
DeptDescription = "Contamination NEC"
},
new HrDeptModel
{
DeptName = "Business Development",
DeptCode = "BD",
Order = 1,
IsActive = true,
HasSubDepartments = false,
ParentDeptCode = "HR",
DeptDescription = "Adenovirus infect NOS"
},
new HrDeptModel
{
DeptName = "Training",
DeptCode = "T",
Order = 3,
IsActive = true,
HasSubDepartments = false,
ParentDeptCode = "HR",
DeptDescription = "TB peritonitis-histo dx"
}
}
},
new HrDeptModel
{
DeptName = "Services",
DeptCode = "S",
Order = 1,
IsActive = true,
HasSubDepartments = true,
ParentDeptCode = "",
DeptDescription = "Oth coll stn obj-per NEC"
SubDepts = new List<HrDeptModel>
{
new HrDeptModel
{
DeptName = "Human Resources",
DeptCode = "HR",
Order = 1,
IsActive = true,
HasSubDepartments = true,
ParentDeptCode = "S",
DeptDescription = "Spon abor w pel inf-unsp"
SubDepts = new List<HrDeptModel>
{
new HrDeptModel
{
DeptName = "Research and Development",
DeptCode = "RD",
Order = 2,
IsActive = true,
HasSubDepartments = false,
ParentDeptCode = "HR",
DeptDescription = "Contamination NEC"
},
new HrDeptModel
{
DeptName = "Business Development",
DeptCode = "BD",
Order = 1,
IsActive = true,
HasSubDepartments = false,
ParentDeptCode = "HR",
DeptDescription = "Adenovirus infect NOS"
},
new HrDeptModel
{
DeptName = "Training",
DeptCode = "T",
Order = 3,
IsActive = true,
HasSubDepartments = false,
ParentDeptCode = "HR",
DeptDescription = "TB peritonitis-histo dx"
}
}
}
}
}
}
new HrDeptModel
{
DeptName = "Services",
DeptCode = "S",
Order = 1,
IsActive = true,
HasSubDepartments = true,
ParentDeptCode = "",
DeptDescription = "Oth coll stn obj-per NEC"
SubDepts = new List<HrDeptModel>
{
new HrDeptModel
{
DeptName = "Human Resources",
DeptCode = "HR",
Order = 1,
IsActive = true,
HasSubDepartments = true,
ParentDeptCode = "S",
DeptDescription = "Spon abor w pel inf-unsp"
SubDepts = new List<HrDeptModel>
{
new HrDeptModel
{
DeptName = "Research and Development",
DeptCode = "RD",
Order = 2,
IsActive = true,
HasSubDepartments = false,
ParentDeptCode = "HR",
DeptDescription = "Contamination NEC"
},
new HrDeptModel
{
DeptName = "Business Development",
DeptCode = "BD",
Order = 1,
IsActive = true,
HasSubDepartments = false,
ParentDeptCode = "HR",
DeptDescription = "Adenovirus infect NOS"
},
new HrDeptModel
{
DeptName = "Training",
DeptCode = "T",
Order = 3,
IsActive = true,
HasSubDepartments = false,
ParentDeptCode = "HR",
DeptDescription = "TB peritonitis-histo dx"
}
}
}
}
}
B. 建立部門層級與轉換後的部門 List¶
有了階層化的部門資料,我們就可以建立部門階層資料及包含 DeptLevelCode
,的完整部門資料:
- 逐一處理階層化的部門資料,若該層級還未加入,則新增一筆新的層級資料至
deptLevList
。 - 將部門資料轉換為
DeptModel
型別,並加入DeptLevelCode
。 - 若該部門存在子部門,則將子部門資料以及層級 +1 傳遞給遞迴函式。
最終將回傳所有部門層級資料的 List,以及每個部門具備階層資訊的完整部門 List。
(List<DeptLevelViewModel> deptLevList, List<DeptModel> deptList) BuildDeptLevs(List<HrDeptModel> hierarchy, int level)
{
// 部門層級 List
var deptLevList = new List<DeptLevelViewModel>();
// 部門 List
var deptList = new List<DeptModel>();
foreach (var dept in hierarchy)
{
// 確認不新增重複部門層級
if (!deptLevList.Any(x => x.Seq == level))
{
// 新增當前層級的資料
deptLevList.Add(new DeptLevelViewModel
{
Code = $"lev{level}", // 部門層級代號
Name = $"Level{level}", // 部門層級名稱
Seq = level, // 部門層級的階層與排序 ( 從 1 開始 )
CorpCode = _corpCode, // 公司代號
Active = true // 是否啟用部門層級
});
}
// 轉換部門資料並加入部門層級
deptList.Add(new DeptModel
{
Code = dept.DeptCode, // 部門代號
Name = dept.DeptName, // 部門名稱
Active = true, // 是否啟用部門
IncludeSubDept = dept.HasSubDepartments, // 是否包含子部門
ParentCode = dept.ParentDeptCode, // 父部門代號 ( 可設定為空值,代表無父部門 )
Description = dept.DeptDescription, // 描述
DeptLevelCode = $"lev{level}", // 部門層級代號
Seq = dept.Order - 1 // 部門的排序 ( 從 0 開始 )
});
// 遞迴處理子部門,層級加 1,並將結果加入 deptLevList 與 deptList
if (dept.SubDepts != null && dept.SubDepts.Count > 0)
{
var (deptLevListTemp, deptListTemp) = BuildDeptLevs(dept.SubDepts, level + 1);
deptLevList.AddRange(deptLevListTemp);
deptList.AddRange(deptListTemp);
}
};
return (deptLevList, deptList);
};
回傳的資料
new List<DeptLevelViewModel>()
{
new DeptLevelViewModel
{
"Seq": 1,
"Code": "lev1",
"Name": "Level1",
"Active": true,
"CorpCode": "woni"
},
new DeptLevelViewModel
{
"Seq": 2,
"Code": "lev2",
"Name": "Level2",
"Active": true,
"CorpCode": "woni"
},
new DeptLevelViewModel
{
"Seq": 3,
"Code": "lev3",
"Name": "Level3",
"Active": true,
"CorpCode": "woni"
}
}
new List<DeptLevelViewModel>()
{
new DeptLevelViewModel
{
"Code": "S",
"Name": "Services",
"Active": true,
"IncludeSubDept": true,
"ParentCode": "",
"Description": "Oth coll stn obj-per NEC",
"DeptLevelCode": "lev1",
"Seq": 0,
"SubDepts": []
},
{
"Code": "HR",
"Name": "Human Resources",
"Active": true,
"IncludeSubDept": true,
"ParentCode": "S",
"Description": "Spon abor w pel inf-unsp",
"DeptLevelCode": "lev2",
"Seq": 0,
"SubDepts": []
},
{
"Code": "RD",
"Name": "Research and Development",
"Active": true,
"IncludeSubDept": false,
"ParentCode": "HR",
"Description": "Contamination NEC",
"DeptLevelCode": "lev3",
"Seq": 1,
"SubDepts": []
},
{
"Code": "BD",
"Name": "Business Development",
"Active": true,
"IncludeSubDept": false,
"ParentCode": "HR",
"Description": "Adenovirus infect NOS",
"DeptLevelCode": "lev3",
"Seq": 0,
"SubDepts": []
},
{
"Code": "T",
"Name": "Training",
"Active": true,
"IncludeSubDept": false,
"ParentCode": "HR",
"Description": "TB peritonitis-histo dx",
"DeptLevelCode": "lev3",
"Seq": 2,
"SubDepts": []
}
}
C. 比對與同步部門階層資料¶
若要 同步 部門層級資料,需要將 HR 系統與 UOF X 的部門階層資料做比對:
- 建立三個 List 來存放待處理的資料。
- 取得現有的 UOF X 部門層級資料。
- 建立以
Code
為鍵的字典hrDeptDict
和uofDeptDict
,以加快查找速度。 - 逐一處理
hrDeptLevs
中的每個部門層級資料:- 若
hrDept
已存在於uofDeptDict
中,則加入deptLevelToUpdate
List 進行更新。 - 若
hrDept
不存在於uofDeptDict
中,則加入deptLevelToAdd
List 進行新增。
- 若
- 逐一處理
uofDeptLevs
中的每個部門層級資料:- 若
uofDept
不存在於hrDeptDict
中,則加入deptLevelToDeactivate
List 進行停用。
- 若
- 使用非同步方法處理 List 中需要更新、新增與停用的部門層級資料。
如此一來,部門層級資料會被分為 需更新、需新增 與 需停用 三種 List。
async Task MapDeptLevels(List<DeptLevelViewModel> hrDeptLevs)
{
// 更新 List
var deptLevelToUpdate = new List<DeptLevelViewModel>();
// 新增 List
var deptLevelToAdd = new List<DeptLevelViewModel>();
// 停用 List
var deptLevelToDeactivate = new List<DeptLevelViewModel>();
// 取得部門層級資料
var uofDeptLevs = await UofxService.BASE.DeptLevel.Get(_corpCode);
// 建立以 Code 為鍵的字典以加快查找速度
var hrDeptDict = hrDeptLevs.ToDictionary(x => x.Code, x => x);
var uofDeptDict = uofDeptLevs.ToDictionary(x => x.Code, x => x);
// 處裡要更新和新增的項目
foreach (var hrDept in hrDeptLevs)
{
// 若此項目已存在,加入更新 List
if (uofDeptDict.ContainsKey(hrDept.Code))
{
deptLevelToUpdate.Add(hrDept);
}
// 此項目不存在,加入新增 List
else
{
deptLevelToAdd.Add(hrDept);
}
};
// 處理要停用的項目
foreach (var uofDept in uofDeptLevs)
{
// 若此項目不存在於 HR 系統,加入停用 List
if (!hrDeptDict.ContainsKey(uofDept.Code))
{
deptLevelToDeactivate.Add(uofDept);
};
};
// 更新部門層級
await UpdateDeptLevels(deptLevelToUpdate);
// 新增部門層級
await CreateDeptLevels(deptLevelToAdd);
// 停用部門層級
await DeactivateDeptLevels(deptLevelToDeactivate);
};
更新、新增與停用部門階層的方法
async Task UpdateDeptLevels(List<DeptLevelViewModel> deptLevels)
{
foreach (var deptLevel in deptLevels)
{
try
{
// 更新部門層級狀態
var updateStatus = await UofxService.BASE.DeptLevel.UpdateStatus(new DeptLevelUpdateStatusModel()
{
CorpCode = _corpCode, // 公司代號
Code = deptLevel.Code, // 部門層級代號
Active = true // 是否啟用部門層級
});
// 更新部門層級資料
var update = await UofxService.BASE.DeptLevel.Update(new DeptLevelUpdateModel()
{
CorpCode = _corpCode, // 公司代號
OriginalCode = deptLevel.Code, // 原始部門層級代號 ( 設為部門層級代號 )
Code = deptLevel.Code, // 部門層級代號
Name = deptLevel.Name // 部門層級名稱
});
// 若部門層級狀態、資料更新成功,加入成功訊息
if (updateStatus == true && update == true) successMsg.Add($"已更新 {deptLevel.Code} 部門層級");
}
catch (Exception ex)
{
// 捕捉錯誤並記錄到 errorMsg 中
errorMsg.Add($"無法更新 {deptLevel.Code} 部門層級,error: {ex.Message}");
}
};
};
async Task CreateDeptLevels(List<DeptLevelViewModel> deptLevels)
{
foreach (var deptLevel in deptLevels)
{
try
{
// 新增部門層級
var resualt = await UofxService.BASE.DeptLevel.Create(deptLevel);
// 若新增成功,加入成功訊息
if (resualt == true) successMsg.Add($"已新增 {deptLevel.Code} 部門層級");
}
catch (Exception ex)
{
// 捕捉錯誤並記錄到 errorMsg 中
errorMsg.Add($"無法新增 {deptLevel.Code} 部門層級,error: {ex.Message}");
}
};
};
async Task DeactivateDeptLevels(List<DeptLevelViewModel> deptLevels)
{
foreach (var deptLevel in deptLevels)
{
try
{
// 停用部門層級
var resualt = await UofxService.BASE.DeptLevel.UpdateStatus(new DeptLevelUpdateStatusModel()
{
CorpCode = _corpCode, // 公司代號
Code = deptLevel.Code, // 部門層級代號
Active = false // 停用部門層級
});
// 若停用成功,加入成功訊息
if (resualt == true) successMsg.Add($"已停用 {deptLevel.Code} 部門層級");
}
catch (Exception ex)
{
// 捕捉錯誤並記錄到 errorMsg 中
errorMsg.Add($"無法停用 {deptLevel.Code} 部門層級,error: {ex.Message}");
}
};
};
D. 比對與同步部門資料¶
若要 同步 部門資料,需要將 HR 系統與 UOF X 的部門資料做比對:
- 建立三個 List 來存放待處理的資料。
- 取得現有的 UOF X 部門資料。
- 建立以
Code
為鍵的字典hrDeptDict
和uofDeptDict
,以加快查找速度。 - 逐一處理
hrDepts
中的每個部門資料:- 若
hrDept
已存在於uofDeptDict
中,則加入deptToUpdate
List 進行更新。 - 若
hrDept
不存在於uofDeptDict
中,則加入deptToAdd
List 進行新增。
- 若
- 逐一處理
uofDepts
中的每個部門資料:- 若
uofDept
不存在於hrDeptDict
中,則加入deptToDeactivate
List 進行停用。
- 若
- 使用非同步方法處理 List 中需要更新、新增與停用的部門資料。
如此一來,部門資料會被分為 需更新、需新增 與 需停用 三種 List。
async Task MapDepts(List<DeptModel> hrDepts)
{
// 更新 List
var deptToUpdate = new List<DeptModel>();
// 新增 List
var deptToAdd = new List<DeptModel>();
// 停用 List
var deptToDeactivate = new List<DeptModel>();
// 取得部門資料
var uofDepts = await UofxService.BASE.Department.Get(new DepartmentGetModel() { CorpCode = _corpCode, IncludeSubDept = true });
// 建立以 Code 為鍵的字典以加快查找速度
var hrDeptDict = hrDepts.ToDictionary(x => x.Code, x => x);
var uofDeptDict = uofDepts.ToDictionary(x => x.Code, x => x);
// 處理要更新和新增的項目
foreach (var hrDept in hrDepts)
{
// 若此項目已存在,加入更新 List
if (uofDeptDict.ContainsKey(hrDept.Code))
{
deptToUpdate.Add(new DeptModel
{
ParentCode = hrDept.ParentCode, // 父部門代號 ( 可設定為空值,代表無父部門 )
Code = hrDept.Code, // 部門代號
Name = hrDept.Name, // 部門名稱
DeptLevelCode = hrDept.DeptLevelCode, // 部門層級代號
Description = hrDept.Description, // 描述
Active = hrDept.Active, // 是否啟用部門
Seq = hrDept.Seq // 部門的排序 ( 從 0 開始 )
});
}
// 此項目不存在,加入新增 List
else
{
deptToAdd.Add(new DeptModel
{
ParentCode = hrDept.ParentCode, // 父部門代號 ( 可設定為空值,代表無父部門 )
Code = hrDept.Code, // 部門代號
Name = hrDept.Name, // 部門名稱
DeptLevelCode = hrDept.DeptLevelCode, // 部門層級代號
Description = hrDept.Description, // 描述
Active = hrDept.Active, // 是否啟用部門
Seq = hrDept.Seq // 部門的排序 ( 從 0 開始 )
});
}
};
// 處理要停用的項目
foreach (var uofDept in uofDepts)
{
// 若此項目不存在於 HR 系統,加入停用 List
if (!hrDeptDict.ContainsKey(uofDept.Code))
{
deptToDeactivate.Add(new DeptModel
{
Code = uofDept.Code, // 部門代號
Active = uofDept.Active, // 是否啟用部門
DeptLevelCode = uofDept.DeptLevelCode // 部門層級代號
});
}
};
// 更新部門
await UpdateDepartments(deptToUpdate);
// 新增部門
await CreateDepartments(deptToAdd);
// 停用部門
await DeactivateDepartments(deptToDeactivate);
};
更新、新增與停用部門的方法
async Task UpdateDepartments(List<DeptModel> departments)
{
foreach (var dept in departments)
{
try
{
// 更新部門狀態
var updateStatus = await UofxService.BASE.Department.UpdateState(new DepartmentUpdateStatusModel()
{
CorpCode = _corpCode, // 公司代號
Code = dept.Code, // 部門代號
Active = dept.Active // 是否啟用部門
});
// 更新部門資料
var update = await UofxService.BASE.Department.Update(new DepartmentUpdateModel()
{
CorpCode = _corpCode, // 公司代號
OriginalCode = dept.Code, // 原始部門代號 ( 設為部門代號 )
Code = dept.Code, // 部門代號
Name = dept.Name, // 部門名稱
DeptLevelCode = dept.DeptLevelCode, // 部門層級代號
Description = dept.Description // 描述
});
// 若部門狀態、資料更新成功,加入成功訊息
if (update == true) successMsg.Add($"已更新 {dept.Code} 部門");
}
catch (Exception ex)
{
// 捕捉錯誤並記錄到 errorMsg 中
errorMsg.Add($"無法更新 {dept.Code} 部門,error: {ex.Message}");
}
};
};
async Task CreateDepartments(List<DeptModel> departments)
{
try
{
foreach (var dept in departments)
{
// 新增部門
var result = await UofxService.BASE.Department.Create(new DepartmentCreateModel()
{
ParentCode = dept.ParentCode, // 父部門代號 ( 可設定為空值,代表無父部門 )
CorpCode = _corpCode, // 公司代號
Code = dept.Code, // 部門代號
DeptLevelCode = dept.DeptLevelCode, // 部門層級代號
Name = dept.Name, // 部門名稱
Description = dept.Description // 描述
});
// 若新增成功,加入成功訊息
if (result == true) successMsg.Add($"已新增 {dept.Code} 部門");
};
}
catch (Exception ex)
{
// 捕捉錯誤並記錄到 errorMsg 中
errorMsg.Add($"無法新增 {departments} 部門,error: {ex.Message}");
}
};
async Task DeactivateDepartments(List<DeptModel> departments)
{
foreach (var dept in departments)
{
try
{
// 停用部門
var result = await UofxService.BASE.Department.UpdateState(new DepartmentUpdateStatusModel()
{
CorpCode = _corpCode, // 公司代號
Code = dept.Code, // 部門代號
Active = false // 是否啟用部門
});
// 若停用成功,加入成功訊息
if (result == true) successMsg.Add($"已停用 {dept.Code} 部門");
}
catch (Exception ex)
{
// 捕捉錯誤並記錄到 errorMsg 中
errorMsg.Add($"無法停用 {dept.Code} 部門,error: {ex.Message}");
}
};
};
E. 彙整部門同步函式¶
因為執行多個函式,因此彙整至 SyncDepts
函式中:
async Task SyncDepts(List<HrDeptModel> originHrDepts)
{
// 部門資料階層化
var hierarchyDeptList = BuildHierarchy(originHrDepts);
// 建立部門層級與轉換後的部門 List
var (deptLevList, deptList) = BuildDeptLevs(hierarchyDeptList, 1);
// 比對與同步部門階層資料
await MapDeptLevels(deptLevList);
// 比對與同步部門資料
await MapDepts(deptList);
};
二、同步人員¶
在同步完部門後,即可確保人員中的部門資料已同步完畢,首先一樣針對 HR 系統資料做處理,再將 HR 系統與 UOF X 人員資料進行比對與同步:
A. 同步職稱職務¶
在同步前,需要先確認每筆人員資料中的職稱職務是否存在:
- 取得 HR 系統中的所有職稱(
JobTitleID
)、職務(JobFunctions
)。 - 取得現有的 UOF X 職稱(
uofJobTitles
)、職務(uofJobFuncs
)。 - 使用
Except
找出需要新增的職稱職務:JobTitleCodes
中不存在於uofJobTitles
的項目,將其存入JobTitleToAdd
。JobFuncs
中不存在於uofJobFuncs
的項目,將其存入JobFuncToAdd
。
- 將
JobTitleToAdd
的職稱項目進行新增。 - 將
JobFuncToAdd
的職務項目進行新增。
async Task SyncJobTitlesAndFunctions(List<HrEmplModel> originHrEmpls)
{
// 取得 HR 系統職稱職務
List<string> JobTitleCodes = originHrEmpls.Select(e => e.JobTitleID).ToList();
List<string> JobFuncs = originHrEmpls.SelectMany(e => e.JobFunctions ?? new List<string>()).ToList();
// 取得 UOF X 系統職稱職務
var uofJobTitles = await UofxService.BASE.JobTitle.Get(_corpCode);
var uofJobFuncs = await UofxService.BASE.JobFunc.Get(_corpCode);
// 找出要新增的職稱職務
var JobTitleToAdd = JobTitleCodes.Except(uofJobTitles.Select(j => j.Code)).ToList();
var JobFuncToAdd = JobFuncs.Except(uofJobFuncs.Select(j => j.Code)).ToList();
foreach (var jobTitle in JobTitleToAdd)
{
try
{
// 新增職稱
var result = await UofxService.BASE.JobTitle.Create(new JobTitleViewModel()
{
CorpCode = _corpCode, // 職稱代號
Rank = 50, // 職稱階層 (1)
Seq = 1, // 職稱排序 ( 從 1 開始 )
Code = jobTitle, // 職稱代號
Title = jobTitle, // 職稱名稱
Active = true // 啟用職稱
});
// 若新增成功,加入成功訊息
if (result == true) successMsg.Add($"已新增 {jobTitle} 職稱");
}
catch (Exception ex)
{
// 捕捉錯誤並記錄到 errorMsg 中
errorMsg.Add($"無法新增 {jobTitle} 職稱,error: {ex.Message}");
}
};
foreach (var jobFunc in JobFuncToAdd)
{
try
{
// 新增職務
var result = await UofxService.BASE.JobFunc.Create(new JobFuncViewModel()
{
CorpCode = _corpCode, // 職稱代號
CategoryName = jobFunc, // 職務類別名稱
Code = jobFunc, // 職務代號
JobFunc = jobFunc, // 職務名稱
Active = true // 啟用職務
});
// 若新增成功,加入成功訊息
if (result == true) successMsg.Add($"已新增 {jobFunc} 職務");
}
catch (Exception ex)
{
// 捕捉錯誤並記錄到 errorMsg 中
errorMsg.Add($"無法新增 {jobFunc} 職務,error: {ex.Message}");
}
};
};
- 因為該範例無法從職稱的字面上判斷出「簽何層級」,因此將簽核層級預設為 50,後續再由管理員到 UOF X 進行調整。
B. 轉換人員 List¶
在進行人員資料的比對前,需要先進行以下處理:
- 以
Account
為 Key 做分組,並將結果存入groupedEmpls
。 - 將人員資料轉換為
EmplModel
。 - 將需要整理的資料做轉換:
Gender
將male
,female
,other
轉換為0
,1
,2
。BirthDate
將 民國年轉換為西元年。
List<EmplModel> ConvertToEmplModels(List<HrEmplModel> originHrEmpls)
{
// 以 Account 作為 key 分組
var groupedEmpls = originHrEmpls.GroupBy(e => e.Account);
// 每一組的分組資料 g 轉換成一個新的 EmplModel
return groupedEmpls.Select(g => new EmplModel
{
CorpCode = _corpCode, // 公司代號
Account = g.Key, // 人員帳號
Name = g.First().Name, // 中文姓名
Gender = g.First().Gender == "male" ? "0" : (g.First().Gender == "female" ? "1" : "2"), // 性別 ( 男性 male => 0, 女性 female => 1, 其他 other => 2 )
EnglishName = g.First().EnglishName, // 英文姓名
EmployeeNumber = g.First().EmployeeID, // 員工編號
ExpiredTime = g.First().ExpiredTime, // 帳號過期時間
ResignationDate = g.First().ResignationDate, // 離職日
IdCardNumber = g.First().IDNumber, // 身份證字號
BirthDate = new DateTime(g.First().BirthDate.Year + 1911, g.First().BirthDate.Month, g.First().BirthDate.Day), // 生日 (民國年轉為西元年)
PhoneNumber = g.First().PhoneNumber, // 行動電話
BusinessCard = g.First().CardTitle, // 名片職稱
HireDate = g.First().HireDate, // 到職日
Email = g.First().PrimaryEmail, // 主要信箱
EmailEx = g.First().SecondaryEmail, // 其他信箱
Active = g.First().AccountActive, // 是否停用人員帳號
Locked = g.First().AccountLocked, // 是否鎖定人員帳號
// 將每個分組中的資料轉換成一個新的 EmpCreateOfDeptItemRequestModel
Depts = g.Select(e => new EmpCreateOfDeptItemRequestModel
{
Code = e.DeptCode, // 部門代號
IsMainDept = e.PrimaryDepartment, // 是否為主要部門
JobTitleCode = e.JobTitleID, // 職稱代號
JobFuncs = e.JobFunctions // 職務代號 ( 可多筆 )
}).ToList(),
// 將每個分組中的主管資料轉換成一個新的 DeptSetManagerModel
Supervisor = g.Where(g => g.IsSupervisor).Select(e => new DeptSetManagerModel
{
CorpCode = _corpCode, // 公司代號
Code = e.DeptCode, // 部門代號
Type = UserType.Account, // 人員的類別 ( 設為 Account )
Value = e.Account // 帳號
}).ToList()
}).ToList();
};
資料的變化
new List<HrEmplModel>
{
new HrEmplModel
{
Account = "costanza",
Name = "松源",
EnglishName = "costanza Posvner",
EmployeeID = "A3897",
ExpiredTime = null,
Gender = "female",
IDNumber = "A123456789",
BirthDate = new DateTime(85, 7, 25),
PhoneNumber = "0946323278",
CardTitle = "Human Resources Assistant II",
HireDate = new DateTime(2013, 12, 21),
ResignationDate = new DateTime(2027, 10, 18),
PrimaryEmail = "cposvner0@jalbum.net",
SecondaryEmail = "cposvner0@phoca.cz",
AccountLocked = false,
AccountActive = true,
JobTitleID = "UX",
JobFunctions = null,
DeptCode = "T",
PrimaryDepartment = true,
IsSupervisor = true
},
new HrEmplModel
{
Account = "natty",
Name = "丰逸",
EnglishName = "natty Yankin",
EmployeeID = "A9618",
ExpiredTime = null,
Gender = "female",
IDNumber = "A123456789",
BirthDate = new DateTime(85, 1, 14),
PhoneNumber = "0934756998",
CardTitle = "Pharmacist",
HireDate = new DateTime(2020, 3, 2),
ResignationDate = null,
PrimaryEmail = "nyankin1@wiley.com",
SecondaryEmail = null,
AccountLocked = false,
AccountActive = true,
JobTitleID = "PM",
JobFunctions = new List<string> { "資訊安全管理" },
DeptCode = "HR",
PrimaryDepartment = true,
IsSupervisor = false
},
new HrEmplModel
{
Account = "costanza",
Name = "松源",
EnglishName = "costanza Posvner",
EmployeeID = "A3897",
ExpiredTime = null,
Gender = "female",
IDNumber = "A123456789",
BirthDate = new DateTime(85, 7, 25),
PhoneNumber = "0946323278",
CardTitle = "Human Resources Assistant II",
HireDate = new DateTime(2013, 12, 21),
ResignationDate = new DateTime(2027, 10, 18),
PrimaryEmail = "cposvner0@jalbum.net",
SecondaryEmail = "cposvner0@phoca.cz",
AccountLocked = false,
AccountActive = true,
JobTitleID = "UX",
JobFunctions = new List<string> { "資訊安全管理" },
DeptCode = "RD",
PrimaryDepartment = false,
IsSupervisor = false
}...
}
[
[
{
"Account": "costanza",
"Name": "松源",
"EnglishName": "costanza Posvner",
"EmployeeID": "A3897",
"ExpiredTime": null,
"Gender": "female",
"IDNumber": "A123456789",
"BirthDate": "0085-07-25T00:00:00",
"PhoneNumber": "0946323278",
"CardTitle": "Human Resources Assistant II",
"HireDate": "2013-12-21T00:00:00",
"ResignationDate": "2027-10-18T00:00:00",
"PrimaryEmail": "cposvner0@jalbum.net",
"SecondaryEmail": "cposvner0@phoca.cz",
"AccountLocked": false,
"AccountActive": true,
"JobTitleID": "UX",
"JobFunctions": null,
"DeptCode": "T",
"PrimaryDepartment": true,
"IsSupervisor": true
},
{
"Account": "costanza",
"Name": "松源",
"EnglishName": "costanza Posvner",
"EmployeeID": "A3897",
"ExpiredTime": null,
"Gender": "female",
"IDNumber": "A123456789",
"BirthDate": "0085-07-25T00:00:00",
"PhoneNumber": "0946323278",
"CardTitle": "Human Resources Assistant II",
"HireDate": "2013-12-21T00:00:00",
"ResignationDate": "2027-10-18T00:00:00",
"PrimaryEmail": "cposvner0@jalbum.net",
"SecondaryEmail": "cposvner0@phoca.cz",
"AccountLocked": false,
"AccountActive": true,
"JobTitleID": "UX",
"JobFunctions": ["資訊安全管理"],
"DeptCode": "RD",
"PrimaryDepartment": false,
"IsSupervisor": false
}
],
[
{
"Account": "natty",
"Name": "丰逸",
"EnglishName": "natty Yankin",
"EmployeeID": "A9618",
"ExpiredTime": null,
"Gender": "female",
"IDNumber": "A123456789",
"BirthDate": "0085-01-14T00:00:00",
"PhoneNumber": "0934756998",
"CardTitle": "Pharmacist",
"HireDate": "2020-03-02T00:00:00",
"ResignationDate": null,
"PrimaryEmail": "nyankin1@wiley.com",
"SecondaryEmail": null,
"AccountLocked": false,
"AccountActive": true,
"JobTitleID": "PM",
"JobFunctions": ["資訊安全管理"],
"DeptCode": "HR",
"PrimaryDepartment": true,
"IsSupervisor": false
}
]...
]
new List<EmplModel>
{
new EmplModel
{
"CorpCode": "woni",
"Account": "costanza",
"Name": "松源",
"EnglishName": "costanza Posvner",
"EmployeeNumber": "A3897",
"ExpiredTime": null,
"ResignationDate": "2027-10-18T00:00:00",
"Gender": "1",
"IdCardNumber": "A123456789",
"BirthDate": "1996-07-25T00:00:00",
"PhoneNumber": "0946323278",
"BusinessCard": "Human Resources Assistant II",
"HireDate": "2013-12-21T00:00:00",
"Email": "cposvner0@jalbum.net",
"EmailEx": "cposvner0@phoca.cz",
"Locked": false,
"Active": true,
"Depts": [
{
"Code": "T",
"IsMainDept": true,
"JobTitleCode": "UX",
"JobFuncs": null
},
{
"Code": "RD",
"IsMainDept": false,
"JobTitleCode": "UX",
"JobFuncs": ["資訊安全管理"]
}
],
"Supervisor": [
{
"Code": "T",
"Type": 0,
"Value": "costanza",
"CorpCode": "woni"
}
]
},
new EmplModel
{
"CorpCode": "woni",
"Account": "natty",
"Name": "丰逸",
"EnglishName": "natty Yankin",
"EmployeeNumber": "A9618",
"ExpiredTime": null,
"ResignationDate": null,
"Gender": "1",
"IdCardNumber": "A123456789",
"BirthDate": "1996-01-14T00:00:00",
"PhoneNumber": "0934756998",
"BusinessCard": "Pharmacist",
"HireDate": "2020-03-02T00:00:00",
"Email": "nyankin1@wiley.com",
"EmailEx": null,
"Locked": false,
"Active": true,
"Depts": [
{
"Code": "HR",
"IsMainDept": true,
"JobTitleCode": "PM",
"JobFuncs": ["資訊安全管理"]
}
],
"Supervisor": []
}...
}
C. 比對與同步人員資料¶
若要 同步 人員資料,需要將 HR 系統與 UOF X 的人員資料做比對:
- 建立兩個 List 來存放待處理的資料。
- 逐一處理
hrEmpls
中的每個部門資料:- 呼叫
UofxService.BASE.OrgEmpl.Get
確認人員是否存在。 - 若
hrEmpl
已存在於 UOF X 中,則加入emplToUpdate
List 進行更新。- 若 現在日期 > 帳號過期時間(
hrEmpl.ExpiredTime
),則加入emplToDeactivate
List 進行停用。
- 若 現在日期 > 帳號過期時間(
- 若
hrEmpl
不存在於 UOF X 中,則加入emplToAdd
List 進行新增。
- 呼叫
- 使用非同步方法處理 List 中需要更新與新增的人員資料。
如此一來,人員資料會被分為 需更新、需新增 與 需停用 三種 List。
async Task MapEmpls(List<EmplModel> hrEmpls)
{
// 更新 List
var emplToUpdate = new List<EmplModel>();
// 新增 List
var emplToAdd = new List<EmplModel>();
// 停用 List
var emplToDeactivate = new List<EmplModel>();
// 將已存在人員放入需更新 List,不存在人員放入需新增 List
foreach (var hrEmpl in hrEmpls)
{
// 取得人員資料
var empl = await UofxService.BASE.OrgEmpl.Get(new EmplQueryRequestModel()
{
CorpCode = _corpCode, // 公司代號
UserType = UserType.Account, // 人員的類別 ( 設為 Account )
UserCode = hrEmpl.Account // 帳號
});
// 若人員存在,加入更新 List
if (empl.Account != null)
{
emplToUpdate.Add(hrEmpl);
}
// 不存在,加入新增 List
else
{
emplToAdd.Add(hrEmpl);
// 如果現在日期大於帳號過期時間,加入停用 List
if (hrEmpl.ExpiredTime != null && DateTime.Now > hrEmpl.ExpiredTime)
{
emplToDeactivate.Add(hrEmpl);
};
}
};
// 更新人員
await UpdateEmpls(emplToUpdate);
// 新增人員
await CreateEmpls(emplToAdd);
// 停用人員
await DeactivateEmpls(emplToDeactivate);
};
更新、新增、停用人員與同步部門主管的方法
async Task UpdateEmpls(List<EmplModel> empls)
{
foreach (var empl in empls)
{
try
{
// 更新人員狀態
var updateStatus = await UofxService.BASE.OrgEmpl.UpdateAcctStatus(new EmplUpdateAcctStatusRequestModel
{
CorpCode = _corpCode, // 公司代號
UserType = UserType.Account, // 人員的類別 ( 設為 Account )
UserCode = empl.Account, // 帳號
Active = empl.Active // 是否停用人員帳號
});
// 更新人員鎖定狀態
var updateLocked = await UofxService.BASE.OrgEmpl.UpdateAcctLocked(new EmplUpdateAcctLockedRequestModel
{
CorpCode = _corpCode, // 公司代號
UserType = UserType.Account, // 人員的類別 ( 設為 Account )
UserCode = empl.Account, // 帳號
Locked = empl.Locked // 是否鎖定人員帳號
});
// 更新人員資料
var update = await UofxService.BASE.OrgEmpl.Update(new EmplUpdateRequestModel
{
CorpCode = _corpCode, // 公司代號
UserType = UserType.Account, // 人員的類別 ( 設為 Account )
UserCode = empl.Account, // 帳號
Account = empl.Account, // 人員帳號
Name = empl.Name, // 中文姓名
EmployeeNumber = empl.EmployeeNumber, // 員工編號
Gender = empl.Gender, // 性別
EnglishName = empl.EnglishName, // 英文姓名
IdCardNumber = empl.IdCardNumber, // 身份證字號
BirthDate = empl.BirthDate, // 生日
PhoneNumber = empl.PhoneNumber, // 行動電話
BusinessCard = empl.BusinessCard, // 名片職稱
HireDate = empl.HireDate, // 到職日
Email = empl.Email, // 主要信箱
EmailEx = empl.EmailEx // 其他信箱
});
// 更新人員過期時間
var updateExpiredTime = await UofxService.BASE.OrgEmpl.UpdateAcctExpiredTime(new EmplUpdateAcctExpiredTimeRequestModel
{
CorpCode = _corpCode, // 公司代號
UserType = UserType.Account, // 人員的類別 ( 設為 Account )
UserCode = empl.Account, // 帳號
ExpiredTime = empl.ExpiredTime // 帳號過期時間
});
// 更新人員離職日期
var updateResignationDate = await UofxService.BASE.OrgEmpl.UpdateEmplResignationDate(new EmplUpdateResignationDateRequestModel
{
CorpCode = _corpCode, // 公司代號
UserType = UserType.Account, // 人員的類別 ( 設為 Account )
UserCode = empl.Account, // 帳號
ResignationDate = empl.ResignationDate // 離職日
});
// 更新人員部門
var updateDepts = await UofxService.BASE.OrgEmpl.UpdateEmplDept(new EmplUpdateDeptRequestModel
{
CorpCode = _corpCode, // 公司代號
UserType = UserType.Account, // 人員的類別 ( 設為 Account )
UserCode = empl.Account, // 帳號
Depts = empl.Depts.Select(d => new DeptRequestModel
{
Code = d.Code, // 部門代號
IsMainDept = d.IsMainDept, // 是否為主要部門
JobTitleCode = d.JobTitleCode, // 職稱代號
JobFuncs = d.JobFuncs // 職務代號 ( 可多筆 )
}).ToList()
});
// 若所有更新皆成功,記錄成功訊息
if (updateStatus == true && updateLocked == true && update == true && updateExpiredTime == true && updateResignationDate == true && updateDepts == true)
{
successMsg.Add($"已更新 {empl.Account} 人員");
};
// 若有部門主管,同步部門主管
if (empl.Supervisor != null && empl.Supervisor.Count > 0)
{
await SyncSuperiors(empl.Supervisor);
};
}
catch (Exception ex)
{
// 捕捉錯誤並記錄到 errorMsg 中
errorMsg.Add($"無法更新 {empl.Account} 人員,error: {ex.Message}");
}
};
};
async Task CreateEmpls(List<EmplModel> empls)
{
foreach (var empl in empls)
{
try
{
// 新增人員
var result = await UofxService.BASE.OrgEmpl.CreateEmpl(new EmpCreateRequestModel
{
CorpCode = _corpCode, // 公司代號
Account = empl.Account, // 人員帳號
Name = empl.Name, // 中文姓名
EnglishName = empl.EnglishName, // 英文姓名
EmployeeNumber = empl.EmployeeNumber, // 員工編號
ExpiredTime = empl.ExpiredTime, // 帳號過期時間
Gender = empl.Gender, // 性別
IdCardNumber = empl.IdCardNumber, // 身份證字號
BirthDate = empl.BirthDate, // 生日
PhoneNumber = empl.PhoneNumber, // 行動電話
BusinessCard = empl.BusinessCard, // 名片職稱
HireDate = empl.HireDate, // 到職日
Email = empl.Email, // 主要信箱
EmailEx = empl.EmailEx, // 其他信箱
Depts = empl.Depts // 所屬部門職務
});
// 若新增成功,加入成功訊息
if (result == true)
{
successMsg.Add($"已新增 {empl.Account} 人員");
// 若有部門主管,同步部門主管
if (empl.Supervisor != null && empl.Supervisor.Count > 0)
{
await SyncSuperiors(empl.Supervisor);
};
};
}
catch (Exception ex)
{
// 捕捉錯誤並記錄到 errorMsg 中
errorMsg.Add($"無法新增 {empl.Account} 人員,error: {ex.Message}");
}
};
};
async Task DeactivateEmpls(List<EmplModel> empls)
{
foreach (var empl in empls)
{
try
{
// 停用人員
var result = await UofxService.BASE.OrgEmpl.UpdateAcctStatus(new EmplUpdateAcctStatusRequestModel
{
CorpCode = _corpCode, // 公司代號
UserType = UserType.Account, // 人員的類別 ( 設為 Account )
UserCode = empl.Account, // 帳號
Active = false // 停用人員帳號
});
// 若停用成功,加入成功訊息
if (result == true) successMsg.Add($"已停用 {empl.Account} 人員");
}
catch (Exception ex)
{
// 捕捉錯誤並記錄到 errorMsg 中
errorMsg.Add($"無法停用 {empl.Account} 人員,error: {ex.Message}");
}
};
};
async Task SyncSuperiors(List<DeptSetManagerModel> Superiors)
{
foreach (var Superior in Superiors)
{
try
{
// 設定部門主管
var result = await UofxService.BASE.Department.Manager.Set(Superior);
// 若設定成功,加入成功訊息
if (result == true) successMsg.Add($"已設定 {Superior.Value} 為 {Superior.Code} 部門主管");
}
catch (Exception ex)
{
// 捕捉錯誤並記錄到 errorMsg 中
errorMsg.Add($"無法設定 {Superior.Value} 為 {Superior.Code} 部門主管,error: {ex.Message}");
}
};
};
D. 彙整人員同步函式¶
因為執行多個函式,因此彙整至 SyncEmpls
函式中:
async Task SyncEmpls(List<HrEmplModel> originHrEmpls)
{
// 同步職稱職務
await SyncJobTitlesAndFunctions(originHrEmpls);
// 轉換人員 List
var hrEmpls = ConvertToEmplModels(originHrEmpls);
// 比對與同步人員資料
await MapEmpls(hrEmpls);
}
三、呼叫同步函式¶
最後,呼叫彙整完的 SyncDepts
與 SyncEmpls
,並將成功與錯誤訊息列印出來。
try
{
// 同步人員部門資料
await SyncDepts(originHrDepts);
await SyncEmpls(originHrEmpls);
// 列印錯誤成功訊息
ConsoleList(errorMsg);
ConsoleList(successMsg);
}
catch (Exception ex)
{
//將 exception 轉換成較容易判斷的 model
var model = UofxService.Error.ConvertToModel(ex);
//將 model 轉成 json 格式印出
Console.WriteLine(UofxService.Json.Convert(model));
}