feat: 增强风险规则生成引擎与预算中心页面

后端拆分风险规则生成为解释器、语义分析、本体对齐等子模块,
优化模板执行和流程图生成,完善员工种子数据和导入逻辑,增强
报销单权限策略和草稿持久化,前端新增预算中心视图和趋势图
组件,重构审计页面和风险规则测试对话框交互,完善文档中心
和报销创建页面细节,补充单元测试覆盖。
This commit is contained in:
caoxiaozhu
2026-05-26 09:15:14 +08:00
parent d0e946cf47
commit 0e861d8fa6
150 changed files with 14953 additions and 4099 deletions

View File

@@ -15,6 +15,8 @@ from app.models.organization import OrganizationUnit
from app.models.role import Role
from app.schemas.employee import EmployeeUpdate
from app.services.employee import EmployeeService
from app.services.employee_seed import CANONICAL_DEPARTMENT_CODES
from app.services.employee_time import format_history_datetime
def build_session() -> Session:
@@ -49,7 +51,7 @@ def test_employee_directory_seeds_rich_employee_data() -> None:
history_count = db.scalar(select(func.count()).select_from(EmployeeChangeLog))
assert role_count == 6
assert org_count == 10
assert org_count == 7
assert employee_count == 30
assert history_count and history_count >= 30
@@ -194,6 +196,42 @@ def test_employee_meta_includes_organization_options() -> None:
assert meta.organizationOptions
assert all(item.code and item.name for item in meta.organizationOptions)
assert [item.name for item in meta.organizationOptions] == [
"人力资源部",
"市场部",
"总裁办",
"技术部",
"生产部",
"财务部",
]
def test_employee_directory_normalizes_legacy_departments() -> None:
with build_session() as db:
service = EmployeeService(db)
service.list_employees()
legacy_department = OrganizationUnit(
unit_code="RND-CENTER",
name="产品研发中心",
unit_type="department",
)
employee = db.execute(
select(Employee).where(Employee.employee_no == "E11745")
).scalar_one()
employee.organization_unit = legacy_department
db.add(legacy_department)
db.commit()
refreshed = next(
item for item in service.list_employees() if item.employeeNo == "E11745"
)
meta = service.get_employee_meta()
assert refreshed.department == "技术部"
assert refreshed.organization is not None
assert refreshed.organization.code == "TECH-DEPT"
assert "RND-CENTER" not in {item.code for item in meta.organizationOptions}
def test_update_employee_changes_organization() -> None:
@@ -202,7 +240,11 @@ def test_update_employee_changes_organization() -> None:
employee = service.list_employees()[0]
organizations = service.repository.list_organization_units()
current_code = employee.organization.code if employee.organization else None
target = next(unit for unit in organizations if unit.unit_code != current_code)
target = next(
unit
for unit in organizations
if unit.unit_code in CANONICAL_DEPARTMENT_CODES and unit.unit_code != current_code
)
updated = service.update_employee(
employee.id,
@@ -245,7 +287,7 @@ def test_update_employee_changes_manager() -> None:
def test_format_history_datetime_uses_local_timezone_without_seconds() -> None:
value = datetime(2026, 5, 20, 6, 30, 45, tzinfo=UTC)
formatted = EmployeeService._format_history_datetime(value)
formatted = format_history_datetime(value)
assert formatted == "2026年5月20日14时30分"
assert "" not in formatted