fix: resolve legal article refs by source aliases
This commit is contained in:
@@ -1715,6 +1715,7 @@ class ReviewService:
|
||||
source_titles, article_nos, keywords = LegalArticleService._extract_runtime_search_terms(risk_text)
|
||||
candidates: List[Any] = []
|
||||
try:
|
||||
candidates.extend(self._search_rule_target_legal_article_candidates(risk_text, tenant_id))
|
||||
if source_titles or article_nos:
|
||||
candidates.extend(
|
||||
self.legal_article_repository.search_published_for_runtime(
|
||||
@@ -1745,6 +1746,26 @@ class ReviewService:
|
||||
return []
|
||||
return self._merge_legal_article_candidates(candidates)
|
||||
|
||||
def _search_rule_target_legal_article_candidates(self, risk_text: str, tenant_id: str) -> List[Any]:
|
||||
candidates: List[Any] = []
|
||||
for rule in LEGAL_ARTICLE_REFERENCE_RULES:
|
||||
matched_trigger = next((trigger for trigger in rule["triggers"] if trigger in risk_text), None)
|
||||
if not matched_trigger:
|
||||
continue
|
||||
for source_id, article_no in rule["targets"]:
|
||||
source_titles = LEGAL_SOURCE_ALIAS_MAP.get(source_id, [])
|
||||
if not source_titles or not article_no:
|
||||
continue
|
||||
candidates.extend(
|
||||
self.legal_article_repository.search_published_for_runtime(
|
||||
tenant_id,
|
||||
source_titles=source_titles,
|
||||
article_nos=[article_no],
|
||||
limit=10,
|
||||
)
|
||||
)
|
||||
return self._merge_legal_article_candidates(candidates)
|
||||
|
||||
@staticmethod
|
||||
def _merge_legal_article_candidates(*groups: List[Any]) -> List[Any]:
|
||||
merged: List[Any] = []
|
||||
@@ -1761,6 +1782,7 @@ class ReviewService:
|
||||
def _resolve_legal_article_refs_for_risk(self, item: Dict[str, Any], candidates: List[Any], by_source_article: Dict[tuple, Any]) -> List[Dict[str, Any]]:
|
||||
risk_text = self._build_legal_reference_search_text(item)
|
||||
selected: List[tuple[Any, str]] = []
|
||||
by_alias_article = self._build_legal_article_alias_index(candidates)
|
||||
|
||||
for article in candidates:
|
||||
if self._is_direct_legal_article_match(risk_text, article):
|
||||
@@ -1771,7 +1793,7 @@ class ReviewService:
|
||||
if not matched_trigger:
|
||||
continue
|
||||
for target in rule["targets"]:
|
||||
article = by_source_article.get(target)
|
||||
article = by_source_article.get(target) or by_alias_article.get(target)
|
||||
if article is not None:
|
||||
selected.append((article, f"命中风险关键词:{matched_trigger}"))
|
||||
|
||||
@@ -1789,6 +1811,23 @@ class ReviewService:
|
||||
break
|
||||
return refs
|
||||
|
||||
def _build_legal_article_alias_index(self, candidates: List[Any]) -> Dict[tuple, Any]:
|
||||
by_alias_article: Dict[tuple, Any] = {}
|
||||
for article in candidates:
|
||||
source_title = str(getattr(article, "source_title", "") or "").lower()
|
||||
article_no = getattr(article, "article_no", None)
|
||||
if not source_title or not article_no:
|
||||
continue
|
||||
for source_id, aliases in LEGAL_SOURCE_ALIAS_MAP.items():
|
||||
names = [source_id, *(aliases or [])]
|
||||
if any(name and self._is_same_legal_source_name(source_title, str(name).lower()) for name in names):
|
||||
by_alias_article[(source_id, article_no)] = article
|
||||
return by_alias_article
|
||||
|
||||
@staticmethod
|
||||
def _is_same_legal_source_name(source_title: str, alias: str) -> bool:
|
||||
return bool(alias and (alias in source_title or source_title in alias))
|
||||
|
||||
def _build_legal_reference_search_text(self, item: Dict[str, Any]) -> str:
|
||||
evidence = item.get("evidence") or {}
|
||||
parts = [
|
||||
|
||||
@@ -8,7 +8,7 @@ from reportlab.pdfbase import pdfmetrics
|
||||
from reportlab.pdfbase.cidfonts import UnicodeCIDFont
|
||||
from reportlab.pdfgen import canvas
|
||||
|
||||
from app.services.review_service import ReviewService
|
||||
from app.services.review_service import LEGAL_ARTICLE_REFERENCE_RULES, LEGAL_SOURCE_ALIAS_MAP, ReviewService
|
||||
|
||||
|
||||
class StubRepository:
|
||||
@@ -366,6 +366,36 @@ class ReviewServiceTestCase(unittest.TestCase):
|
||||
self.assertEqual(enriched[0]["legal_article_refs"][0]["detail_path"], "/legal-articles?article_id=lart-cyber-001")
|
||||
self.assertIn("第一条", enriched[0]["legal_article_refs"][0]["article_no"])
|
||||
|
||||
def test_legal_article_refs_resolve_imported_source_alias_targets(self):
|
||||
source_id, article_no = LEGAL_ARTICLE_REFERENCE_RULES[0]["targets"][0]
|
||||
trigger = LEGAL_ARTICLE_REFERENCE_RULES[0]["triggers"][0]
|
||||
source_title = LEGAL_SOURCE_ALIAS_MAP[source_id][-1]
|
||||
self.service.legal_article_repository.reference_candidate_count = 0
|
||||
self.service.legal_article_repository.items = [
|
||||
StubLegalArticle(
|
||||
"lart-civil-585-full",
|
||||
"FLK-CIVIL-CODE-FULL",
|
||||
source_title,
|
||||
article_no,
|
||||
["official-law"],
|
||||
f"{trigger} full article text",
|
||||
)
|
||||
]
|
||||
risk_item = {
|
||||
"title": f"{trigger} risk needs review",
|
||||
"level": "high",
|
||||
"description": trigger,
|
||||
"suggestion": "complete clause",
|
||||
"legal_basis": "",
|
||||
"evidence": {"quote": trigger, "clause_title": "liability"},
|
||||
}
|
||||
|
||||
enriched = self.service._attach_legal_article_refs([risk_item], self.current_user["tenant_id"])
|
||||
|
||||
self.assertEqual(enriched[0]["legal_article_refs"][0]["id"], "lart-civil-585-full")
|
||||
self.assertEqual(enriched[0]["legal_article_refs"][0]["source_id"], "FLK-CIVIL-CODE-FULL")
|
||||
self.assertEqual(enriched[0]["legal_article_refs"][0]["article_no"], article_no)
|
||||
|
||||
def test_report_metadata_and_content_match_review(self):
|
||||
created = self.service.create_review_task(
|
||||
tenant_id=self.current_user["tenant_id"],
|
||||
|
||||
Reference in New Issue
Block a user