feat: 扩展风险规则体系、审批动态路由与预算中心列表化改造

- 新增 25+ 条风险规则(预算/报销/申请/通用类),完善风险规则模拟与反馈发布机制
- 引入费用审批动态路由、平台风险分级、预审与风险阶段管理
- 预算中心列表化改造,优化票据夹仪表盘与数字员工工作看板
- 新增 Hermes 风险线索收集器、Agent 链路追踪中心
- 扩展数字员工能力库(18 个领域 Skill)与交通费用自动预估
- 完善报销申请快速预览、权限控制与前端测试覆盖
This commit is contained in:
caoxiaozhu
2026-06-01 17:07:14 +08:00
parent 7989f3a159
commit 92444e7eae
285 changed files with 25075 additions and 2986 deletions

View File

@@ -81,6 +81,20 @@ class AuthService:
session = UserSessionMetricService(self.db).start_session(user)
return LoginResponse(user=self._serialize_user(user), sessionId=session.session_id)
def get_user_snapshot(self, identifier: str) -> AuthUserRead | None:
normalized = identifier.strip()
if not normalized or not self.settings.setup_completed:
return None
employee = self._find_employee_by_email(normalized)
if employee is None:
EmployeeService(self.db).ensure_directory_ready()
employee = self._find_employee_by_email(normalized)
if employee is None or employee.employment_status == "停用":
return None
return self._serialize_user(self._build_employee_user(employee))
def _authenticate_admin(self, identifier: str, password: str) -> AuthenticatedUser | None:
record = SettingsService(self.db).verify_admin_login(identifier, password)
if record is None:
@@ -114,17 +128,7 @@ class AuthService:
return None
EmployeeService(self.db).ensure_directory_ready()
stmt = (
select(Employee)
.options(
selectinload(Employee.organization_unit),
selectinload(Employee.manager),
selectinload(Employee.roles),
)
.where(func.lower(Employee.email) == identifier.lower())
)
employee = self.db.execute(stmt).scalars().first()
employee = self._find_employee_by_email(identifier)
if employee is None or not employee.password_hash:
return None
@@ -136,6 +140,21 @@ class AuthService:
if not verify_password(password, employee.password_hash):
return None
return self._build_employee_user(employee)
def _find_employee_by_email(self, identifier: str) -> Employee | None:
stmt = (
select(Employee)
.options(
selectinload(Employee.organization_unit),
selectinload(Employee.manager),
selectinload(Employee.roles),
)
.where(func.lower(Employee.email) == identifier.lower())
)
return self.db.execute(stmt).scalars().first()
def _build_employee_user(self, employee: Employee) -> AuthenticatedUser:
sorted_roles = sorted(
list(employee.roles),
key=lambda item: (ROLE_DISPLAY_ORDER.get(item.role_code, 999), item.name),