feat: 优化差旅报销预审流程与个人工作台 UI 体系
- 完善 user_agent_application 申请差旅报销预审槽位与消息组装 - 增强预算助理报告与风险建议卡片交互 - 重构登录页视觉样式与移动端响应式适配 - 优化个人工作台、文档中心、政策中心、员工管理等页面布局 - 拆分 travelRequestDetailPreReviewModel 为 advice/submit 模型 - 补充报销草稿、风险复核、Item Sync 与模板执行器测试覆盖
This commit is contained in:
@@ -14,6 +14,7 @@ CITY_CONSISTENCY_SEMANTIC_TYPES = {
|
||||
"travel_city_consistency",
|
||||
"travel_route_city_consistency",
|
||||
}
|
||||
ROUTE_CITY_SPLIT_PATTERN = re.compile(r"\s*(?:至|到|→|->|-|-|—|~|~|/|、|,|,|;|;)\s*")
|
||||
|
||||
|
||||
class RiskRuleTemplateExecutor:
|
||||
@@ -612,19 +613,32 @@ class RiskRuleTemplateExecutor:
|
||||
) -> list[str]:
|
||||
if len(route_values) < 2:
|
||||
return []
|
||||
allowed = {value.lower() for value in [*reference_values, *home_values] if value}
|
||||
if not allowed:
|
||||
allowed_values = [value for value in [*reference_values, *home_values] if value]
|
||||
if not allowed_values:
|
||||
return []
|
||||
candidates = route_values if home_values else route_values[1:-1]
|
||||
unexpected: list[str] = []
|
||||
for city in candidates:
|
||||
normalized = city.lower()
|
||||
if normalized in allowed:
|
||||
if RiskRuleTemplateExecutor._values_overlap([city], allowed_values):
|
||||
continue
|
||||
if city not in unexpected:
|
||||
unexpected.append(city)
|
||||
return unexpected
|
||||
|
||||
@staticmethod
|
||||
def _expand_route_city_values(values: list[Any]) -> list[Any]:
|
||||
expanded: list[Any] = []
|
||||
for value in values:
|
||||
if isinstance(value, (list, tuple, set)):
|
||||
expanded.extend(RiskRuleTemplateExecutor._expand_route_city_values(list(value)))
|
||||
continue
|
||||
text = str(value or "").strip()
|
||||
if not text:
|
||||
continue
|
||||
parts = [part.strip() for part in ROUTE_CITY_SPLIT_PATTERN.split(text) if part.strip()]
|
||||
expanded.extend(parts if len(parts) >= 2 else [text])
|
||||
return expanded
|
||||
|
||||
def _resolve_attachment_values(
|
||||
self, field_key: str, contexts: list[dict[str, Any]]
|
||||
) -> list[str]:
|
||||
@@ -643,7 +657,7 @@ class RiskRuleTemplateExecutor:
|
||||
else self._scan_document_values(document_info, "city")
|
||||
)
|
||||
elif field_key == "route_cities":
|
||||
values.extend(self._scan_document_values(document_info, field_key))
|
||||
values.extend(self._expand_route_city_values(self._scan_document_values(document_info, field_key)))
|
||||
else:
|
||||
values.extend(self._scan_document_values(document_info, field_key))
|
||||
return self._normalize_values(values)
|
||||
@@ -878,9 +892,9 @@ class RiskRuleTemplateExecutor:
|
||||
left_set = {value.lower() for value in left_values}
|
||||
right_set = {value.lower() for value in right_values}
|
||||
if operator in {"equals", "in", "overlap"}:
|
||||
return bool(left_set & right_set)
|
||||
return RiskRuleTemplateExecutor._values_overlap(left_values, right_values)
|
||||
if operator in {"not_equals", "not_in", "not_overlap"}:
|
||||
return not bool(left_set & right_set)
|
||||
return not RiskRuleTemplateExecutor._values_overlap(left_values, right_values)
|
||||
if operator == "contains_any":
|
||||
return any(any(right in left for right in right_set) for left in left_set)
|
||||
return bool(left_set & right_set)
|
||||
|
||||
Reference in New Issue
Block a user