fix: 修复员工服务、报销单审批及前端交互细节

- 修复员工创建时组织架构关联与邮箱校验逻辑
- 修复报销单API端点参数及预审流程调用
- 优化审批中心、差旅详情等前端页面交互
- 更新侧边栏导航与请求视图模型
- 补充员工服务与报销单相关测试用例
This commit is contained in:
caoxiaozhu
2026-05-20 14:32:35 +08:00
parent d7e98a58b9
commit f8b25a7ccc
14 changed files with 84 additions and 52 deletions

View File

@@ -1,8 +1,9 @@
from __future__ import annotations
from collections import Counter
from datetime import date, datetime
from datetime import UTC, date, datetime
from typing import Any
from zoneinfo import ZoneInfo
from sqlalchemy import inspect, select, text
from sqlalchemy.orm import Session
@@ -49,6 +50,7 @@ from app.services.employee_seed import (
logger = get_logger("app.services.employee")
DEFAULT_EMPLOYEE_PASSWORD = "123456"
MAX_EMPLOYEE_CHANGE_LOGS = 5
DISPLAY_TIMEZONE = ZoneInfo("Asia/Shanghai")
STATUS_TONE_MAP = {
"在职": "success",
@@ -183,7 +185,7 @@ class EmployeeService:
sync_state=payload.sync_state,
spotlight=payload.spotlight,
password_hash=hash_password(DEFAULT_EMPLOYEE_PASSWORD),
last_sync_at=datetime.now(),
last_sync_at=datetime.now(UTC),
)
if payload.organization_unit_code:
@@ -360,7 +362,7 @@ class EmployeeService:
if not changed_fields and not password_changed and not role_changed:
return self._serialize_employee(employee)
now = datetime.now()
now = datetime.now(UTC)
employee.last_sync_at = now
employee.sync_state = "已同步"
@@ -401,7 +403,7 @@ class EmployeeService:
if employee.employment_status == "停用":
return self._serialize_employee(employee)
now = datetime.now()
now = datetime.now(UTC)
employee.employment_status = "停用"
employee.sync_state = "已同步"
employee.last_sync_at = now
@@ -422,7 +424,7 @@ class EmployeeService:
if employee.employment_status != "停用":
return self._serialize_employee(employee)
now = datetime.now()
now = datetime.now(UTC)
employee.employment_status = "在职"
employee.sync_state = "已同步"
employee.last_sync_at = now
@@ -484,7 +486,7 @@ class EmployeeService:
logger.exception("Employee import failed during database write")
raise
imported_at = self._format_datetime(datetime.now()) or ""
imported_at = self._format_datetime(datetime.now(UTC)) or ""
message = f"导入成功:新增 {summary['created']} 人,更新 {summary['updated']} 人。"
logger.info(
"Imported employees created=%d updated=%d total=%d",
@@ -624,7 +626,7 @@ class EmployeeService:
}
created = 0
updated = 0
now = datetime.now()
now = datetime.now(UTC)
try:
for row in rows:
@@ -979,7 +981,7 @@ class EmployeeService:
employee=employee,
action=action,
owner=owner,
occurred_at=occurred_at or datetime.now(),
occurred_at=occurred_at or datetime.now(UTC),
)
)
@@ -1106,19 +1108,29 @@ class EmployeeService:
return None
return value.strftime("%Y-%m-%d")
@staticmethod
def _to_display_datetime(value: datetime) -> datetime:
if value.tzinfo is None:
normalized = value.replace(tzinfo=UTC)
else:
normalized = value.astimezone(UTC)
return normalized.astimezone(DISPLAY_TIMEZONE)
@staticmethod
def _format_datetime(value: datetime | None) -> str | None:
if value is None:
return None
return value.strftime("%Y-%m-%d %H:%M")
local = EmployeeService._to_display_datetime(value)
return local.strftime("%Y-%m-%d %H:%M")
@staticmethod
def _format_history_datetime(value: datetime | None) -> str:
if value is None:
return ""
local = EmployeeService._to_display_datetime(value)
return (
f"{value.year}{value.month}{value.day}"
f"{value.hour}{value.minute}{value.second}"
f"{local.year}{local.month}{local.day}"
f"{local.hour}{local.minute}"
)
@staticmethod