diff --git a/README.md b/README.md index 2797ec7..541552d 100644 --- a/README.md +++ b/README.md @@ -121,12 +121,18 @@ SQLSERVER_ENCRYPT=false SQLSERVER_TRUST_CERT=true HR_SYNC_ENABLED=false HR_SYNC_SCHEDULE=03:00 +HR_SYNC_ALLOWED_STATUS_VALUES=3 HR_SYNC_INACTIVE_STATUS_VALUES=5 +HR_SYNC_EXCLUDED_DEPARTMENTS=供应商,快递公司,外部交流 +HR_SYNC_EXCLUDED_NAME_KEYWORDS= ``` - `HR_SYNC_ENABLED=true` 时,后端会启动每日定时同步 - `HR_SYNC_SCHEDULE` 建议使用 `03:00` +- `HR_SYNC_ALLOWED_STATUS_VALUES` 用来限定“哪些源状态算正式员工”,当前建议 `3` - `HR_SYNC_INACTIVE_STATUS_VALUES` 用来标记源系统里的非在职状态码 +- `HR_SYNC_EXCLUDED_DEPARTMENTS` 用来排除供应商、快递公司等外部对象 +- `HR_SYNC_EXCLUDED_NAME_KEYWORDS` 可选,用来额外排除名字特征 - 如果源库是较老版本 SQL Server,建议 `SQLSERVER_ENCRYPT=false` ## 当前线上资源 diff --git a/backend/src/services/hrSyncService.ts b/backend/src/services/hrSyncService.ts index 5749659..887de48 100644 --- a/backend/src/services/hrSyncService.ts +++ b/backend/src/services/hrSyncService.ts @@ -43,6 +43,14 @@ let schedulerStarted = false let schedulerRunning = false let lastSchedulerRunKey = '' +const getConfiguredSet = (value: string, defaultValue: string) => + new Set( + (value || defaultValue) + .split(',') + .map((item) => item.trim()) + .filter(Boolean) + ) + const getSqlServerConfig = (): any => { const server = process.env.SQLSERVER_HOST const port = Number(process.env.SQLSERVER_PORT || '1433') @@ -106,11 +114,9 @@ const parseExtensionFlowerName = (extension: unknown) => { } const resolvePersonStatus = (active: unknown, employeeStatus: unknown): 'active' | 'inactive' => { - const inactiveStatusValues = new Set( - (process.env.HR_SYNC_INACTIVE_STATUS_VALUES || '5') - .split(',') - .map((item) => item.trim()) - .filter(Boolean) + const inactiveStatusValues = getConfiguredSet( + process.env.HR_SYNC_INACTIVE_STATUS_VALUES || '', + '5' ) const sourceStatus = safeTrim(employeeStatus) @@ -130,6 +136,41 @@ const resolvePersonStatus = (active: unknown, employeeStatus: unknown): 'active' return 'active' } +const isFormalEmployee = (person: SourcePerson) => { + const allowedStatusValues = getConfiguredSet( + process.env.HR_SYNC_ALLOWED_STATUS_VALUES || '', + '3' + ) + const excludedDepartments = Array.from( + getConfiguredSet( + process.env.HR_SYNC_EXCLUDED_DEPARTMENTS || '', + '供应商,快递公司,外部交流' + ) + ) + const excludedNameKeywords = Array.from( + getConfiguredSet( + process.env.HR_SYNC_EXCLUDED_NAME_KEYWORDS || '', + '' + ) + ) + + if (!allowedStatusValues.has(person.sourceEmployeeStatus || '')) { + return false + } + + const departmentName = person.departmentName || '' + if (excludedDepartments.some((keyword) => departmentName.includes(keyword))) { + return false + } + + const text = `${person.nickname} ${person.realName}`.trim() + if (excludedNameKeywords.some((keyword) => text.includes(keyword))) { + return false + } + + return person.status === 'active' +} + const getSourceDepartments = async (pool: any): Promise => { const result = await pool .request() @@ -207,6 +248,7 @@ const getSourcePersons = async (pool: any): Promise => { } satisfies SourcePerson }) .filter((item: SourcePerson) => item.sourceUserId) + .filter((item: SourcePerson) => isFormalEmployee(item)) } const syncDepartments = async (sourceDepartments: SourceDepartment[]) => { diff --git a/docker-compose.yml b/docker-compose.yml index 4932f87..3d56dcf 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -45,7 +45,10 @@ services: SQLSERVER_TRUST_CERT: ${SQLSERVER_TRUST_CERT:-true} HR_SYNC_ENABLED: ${HR_SYNC_ENABLED:-false} HR_SYNC_SCHEDULE: ${HR_SYNC_SCHEDULE:-03:00} + HR_SYNC_ALLOWED_STATUS_VALUES: ${HR_SYNC_ALLOWED_STATUS_VALUES:-3} HR_SYNC_INACTIVE_STATUS_VALUES: ${HR_SYNC_INACTIVE_STATUS_VALUES:-5} + HR_SYNC_EXCLUDED_DEPARTMENTS: ${HR_SYNC_EXCLUDED_DEPARTMENTS:-供应商,快递公司,外部交流} + HR_SYNC_EXCLUDED_NAME_KEYWORDS: ${HR_SYNC_EXCLUDED_NAME_KEYWORDS:-} depends_on: mysql: condition: service_healthy