2026-05-22 10:42:31 +08:00
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
from collections.abc import Callable
|
|
|
|
|
from datetime import date, datetime
|
|
|
|
|
|
|
|
|
|
from app.models.employee import Employee
|
|
|
|
|
from app.models.employee_change_log import EmployeeChangeLog
|
|
|
|
|
from app.models.role import Role
|
|
|
|
|
from app.schemas.employee import (
|
|
|
|
|
EmployeeHistoryRead,
|
|
|
|
|
EmployeeOrganizationRead,
|
|
|
|
|
EmployeeRead,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def serialize_employee(
|
|
|
|
|
employee: Employee,
|
|
|
|
|
*,
|
|
|
|
|
sorted_roles: list[Role],
|
|
|
|
|
sorted_change_logs: list[EmployeeChangeLog],
|
|
|
|
|
format_date: Callable[[date | None], str | None],
|
|
|
|
|
format_datetime: Callable[[datetime | None], str | None],
|
|
|
|
|
format_history_datetime: Callable[[datetime | None], str],
|
|
|
|
|
role_permission_map: dict[str, list[str]],
|
|
|
|
|
status_tone_map: dict[str, str],
|
|
|
|
|
max_change_logs: int,
|
|
|
|
|
) -> EmployeeRead:
|
|
|
|
|
organization = employee.organization_unit
|
|
|
|
|
role_labels = [role.name for role in sorted_roles]
|
|
|
|
|
role_codes = [role.role_code for role in sorted_roles]
|
|
|
|
|
|
|
|
|
|
history = [
|
|
|
|
|
EmployeeHistoryRead(
|
|
|
|
|
action=item.action,
|
|
|
|
|
owner=item.owner,
|
|
|
|
|
time=format_history_datetime(item.occurred_at),
|
|
|
|
|
occurredAt=format_history_datetime(item.occurred_at),
|
|
|
|
|
)
|
|
|
|
|
for item in sorted_change_logs[:max_change_logs]
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
return EmployeeRead(
|
|
|
|
|
id=employee.id,
|
|
|
|
|
avatar=(employee.name or "?")[:1],
|
|
|
|
|
name=employee.name,
|
|
|
|
|
employeeNo=employee.employee_no,
|
|
|
|
|
department=organization.name if organization else "",
|
|
|
|
|
position=employee.position,
|
|
|
|
|
grade=employee.grade,
|
|
|
|
|
manager=employee.manager.name if employee.manager else "CEO",
|
|
|
|
|
managerEmployeeNo=employee.manager.employee_no if employee.manager else None,
|
|
|
|
|
financeOwner=employee.finance_owner_name or "",
|
|
|
|
|
roles=role_labels,
|
|
|
|
|
roleCodes=role_codes,
|
|
|
|
|
status=employee.employment_status,
|
|
|
|
|
statusTone=status_tone_map.get(employee.employment_status, "neutral"),
|
|
|
|
|
gender=employee.gender,
|
|
|
|
|
age=calculate_age(employee.birth_date),
|
|
|
|
|
birthDate=format_date(employee.birth_date),
|
|
|
|
|
email=employee.email,
|
|
|
|
|
phone=employee.phone,
|
|
|
|
|
joinDate=format_date(employee.join_date),
|
|
|
|
|
location=employee.location,
|
|
|
|
|
costCenter=employee.cost_center,
|
2026-05-28 12:09:49 +08:00
|
|
|
bankName=employee.bank_name,
|
|
|
|
|
bankAccountNo=employee.bank_account_no,
|
|
|
|
|
bankAccountName=employee.bank_account_name,
|
2026-05-22 10:42:31 +08:00
|
|
|
updatedAt=format_datetime(employee.updated_at or employee.created_at),
|
|
|
|
|
lastSync=format_datetime(employee.last_sync_at),
|
|
|
|
|
syncState=employee.sync_state,
|
|
|
|
|
spotlight=employee.spotlight,
|
|
|
|
|
permissions=collect_permissions(role_codes, role_permission_map),
|
|
|
|
|
history=history,
|
|
|
|
|
organization=(
|
|
|
|
|
EmployeeOrganizationRead(
|
|
|
|
|
id=organization.id,
|
|
|
|
|
code=organization.unit_code,
|
|
|
|
|
name=organization.name,
|
|
|
|
|
unitType=organization.unit_type,
|
|
|
|
|
costCenter=organization.cost_center,
|
|
|
|
|
location=organization.location,
|
|
|
|
|
managerName=organization.manager_name,
|
|
|
|
|
)
|
|
|
|
|
if organization
|
|
|
|
|
else None
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def collect_permissions(
|
|
|
|
|
role_codes: list[str],
|
|
|
|
|
role_permission_map: dict[str, list[str]],
|
|
|
|
|
) -> list[str]:
|
|
|
|
|
permissions: list[str] = []
|
|
|
|
|
seen: set[str] = set()
|
|
|
|
|
|
|
|
|
|
for role_code in role_codes:
|
|
|
|
|
for permission in role_permission_map.get(role_code, []):
|
|
|
|
|
if permission in seen:
|
|
|
|
|
continue
|
|
|
|
|
permissions.append(permission)
|
|
|
|
|
seen.add(permission)
|
|
|
|
|
|
|
|
|
|
return permissions
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def format_history_datetime(
|
|
|
|
|
value: datetime | None,
|
|
|
|
|
*,
|
|
|
|
|
to_display_datetime: Callable[[datetime], datetime],
|
|
|
|
|
) -> str:
|
|
|
|
|
if value is None:
|
|
|
|
|
return ""
|
|
|
|
|
local = to_display_datetime(value)
|
|
|
|
|
return (
|
|
|
|
|
f"{local.year}年{local.month}月{local.day}日"
|
|
|
|
|
f"{local.hour}时{local.minute}分"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def calculate_age(birth_date: date | None) -> int | None:
|
|
|
|
|
if birth_date is None:
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
today = date.today()
|
|
|
|
|
age = today.year - birth_date.year
|
|
|
|
|
if (today.month, today.day) < (birth_date.month, birth_date.day):
|
|
|
|
|
age -= 1
|
|
|
|
|
return age
|