feat: 新增预算中心本体与风险规则评分回填
后端新增预算本体解析模块和风险规则评分回填服务,优化规则 生成本体对齐和提示词构建,增强费用类型关键词和本体验证, 完善报销查询和审计接口,前端预算中心页面增加对话框和本 体工具函数,重构审计页面元数据和视图模型,补充单元测试。
This commit is contained in:
@@ -3,16 +3,15 @@ from __future__ import annotations
|
||||
from collections.abc import Generator
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import Session, sessionmaker
|
||||
from sqlalchemy.pool import StaticPool
|
||||
|
||||
from app.api.deps import get_db
|
||||
from app.db.base import Base
|
||||
from app.main import create_app
|
||||
from app.schemas.ontology import OntologyParseRequest
|
||||
from app.services.ontology import LlmOntologyParseResult, SemanticOntologyService
|
||||
from fastapi.testclient import TestClient
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import Session, sessionmaker
|
||||
from sqlalchemy.pool import StaticPool
|
||||
|
||||
from app.api.deps import get_db
|
||||
from app.db.base import Base
|
||||
from app.schemas.ontology import OntologyParseRequest
|
||||
from app.services.ontology import LlmOntologyParseResult, SemanticOntologyService
|
||||
|
||||
|
||||
def build_session_factory() -> sessionmaker[Session]:
|
||||
@@ -25,9 +24,11 @@ def build_session_factory() -> sessionmaker[Session]:
|
||||
return sessionmaker(bind=engine, autoflush=False, autocommit=False)
|
||||
|
||||
|
||||
def build_client() -> tuple[TestClient, sessionmaker[Session]]:
|
||||
session_factory = build_session_factory()
|
||||
app = create_app()
|
||||
def build_client() -> tuple[TestClient, sessionmaker[Session]]:
|
||||
session_factory = build_session_factory()
|
||||
from app.main import create_app
|
||||
|
||||
app = create_app()
|
||||
|
||||
def override_db() -> Generator[Session, None, None]:
|
||||
db = session_factory()
|
||||
@@ -253,13 +254,113 @@ def test_semantic_ontology_service_extracts_entities_time_and_constraints() -> N
|
||||
user_id="pytest",
|
||||
)
|
||||
)
|
||||
|
||||
assert result.scenario == "expense"
|
||||
|
||||
assert result.scenario == "expense"
|
||||
assert result.intent == "query"
|
||||
assert result.time_range.start_date == "2026-04-01"
|
||||
assert result.time_range.end_date == "2026-04-30"
|
||||
|
||||
|
||||
def test_semantic_ontology_service_extracts_budget_query_fields() -> None:
|
||||
session_factory = build_session_factory()
|
||||
with session_factory() as db:
|
||||
result = SemanticOntologyService(db).parse(
|
||||
OntologyParseRequest(
|
||||
query="查询 CC-4100 2026年度差旅费可用预算和预算占用",
|
||||
user_id="pytest",
|
||||
)
|
||||
)
|
||||
|
||||
entity_map = {item.type: item.normalized_value for item in result.entities}
|
||||
metric_names = {item.name for item in result.metrics}
|
||||
|
||||
assert result.scenario == "budget"
|
||||
assert result.intent == "query"
|
||||
assert entity_map["cost_center"] == "CC-4100"
|
||||
assert entity_map["budget_period"] == "2026年度"
|
||||
assert entity_map["budget_subject"] == "travel"
|
||||
assert entity_map["expense_type"] == "travel"
|
||||
assert {"available_amount", "reserved_amount"}.issubset(metric_names)
|
||||
|
||||
|
||||
def test_semantic_ontology_service_extracts_budget_edit_fields() -> None:
|
||||
session_factory = build_session_factory()
|
||||
with session_factory() as db:
|
||||
result = SemanticOntologyService(db).parse(
|
||||
OntologyParseRequest(
|
||||
query="编辑预算:2026年度 CC-4100 差旅费预算金额60万元,预警线80%,控制动作提醒",
|
||||
user_id="pytest",
|
||||
context_json={
|
||||
"document_type": "budget_plan",
|
||||
"entry_source": "budget_center",
|
||||
"conversation_scenario": "budget",
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
entity_map = {item.type: item.normalized_value for item in result.entities}
|
||||
|
||||
assert result.scenario == "budget"
|
||||
assert result.intent == "draft"
|
||||
assert result.permission.level == "draft_write"
|
||||
assert entity_map["budget_period"] == "2026年度"
|
||||
assert entity_map["budget_subject"] == "travel"
|
||||
assert entity_map["expense_type"] == "travel"
|
||||
assert entity_map["budget_amount"] == "600000"
|
||||
assert entity_map["warning_threshold"] == "80%"
|
||||
assert entity_map["control_action"] == "remind"
|
||||
|
||||
|
||||
def test_semantic_ontology_service_extracts_quarter_budget_period() -> None:
|
||||
session_factory = build_session_factory()
|
||||
with session_factory() as db:
|
||||
result = SemanticOntologyService(db).parse(
|
||||
OntologyParseRequest(
|
||||
query="查询 CC-4100 2026年Q3 住宿费预算金额",
|
||||
user_id="pytest",
|
||||
)
|
||||
)
|
||||
|
||||
entity_map = {item.type: item.normalized_value for item in result.entities}
|
||||
|
||||
assert result.scenario == "budget"
|
||||
assert entity_map["budget_period"] == "2026年Q3"
|
||||
assert entity_map["budget_subject"] == "hotel"
|
||||
assert entity_map["expense_type"] == "hotel"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"query,expected_code,expected_label",
|
||||
[
|
||||
("查询2026年度市场推广费预算余额", "marketing", "市场推广费"),
|
||||
("查看2026年度软件服务费已占用金额", "software", "软件服务费"),
|
||||
("统计2026年度业务招待费预算金额", "meal", "业务招待费"),
|
||||
],
|
||||
)
|
||||
def test_semantic_ontology_service_links_budget_subject_to_expense_type(
|
||||
query: str,
|
||||
expected_code: str,
|
||||
expected_label: str,
|
||||
) -> None:
|
||||
session_factory = build_session_factory()
|
||||
with session_factory() as db:
|
||||
result = SemanticOntologyService(db).parse(
|
||||
OntologyParseRequest(query=query, user_id="pytest")
|
||||
)
|
||||
|
||||
assert result.scenario == "budget"
|
||||
assert any(
|
||||
item.type == "budget_subject" and item.normalized_value == expected_code
|
||||
for item in result.entities
|
||||
)
|
||||
assert any(
|
||||
item.type == "expense_type"
|
||||
and item.normalized_value == expected_code
|
||||
and item.value == expected_label
|
||||
for item in result.entities
|
||||
)
|
||||
|
||||
|
||||
def test_semantic_ontology_service_extracts_new_document_numbers() -> None:
|
||||
session_factory = build_session_factory()
|
||||
with session_factory() as db:
|
||||
|
||||
Reference in New Issue
Block a user