diff --git a/backend/app/services/legal_domain_registry.py b/backend/app/services/legal_domain_registry.py index ca22f093..dcab9ba8 100644 --- a/backend/app/services/legal_domain_registry.py +++ b/backend/app/services/legal_domain_registry.py @@ -464,6 +464,7 @@ RELATION_STATUS_ATTENTION = "attention" RELATION_CONFIDENCE_HIGH = "high" RELATION_CONFIDENCE_WEAK = "weak" LEGAL_ARTICLE_REFERENCE_RULES = [ + # 民法典通用条款 { "triggers": ["违约金"], "targets": [("LAW-CIVIL-CODE-2020", "第五百八十五条"), ("LAW-CIVIL-CODE-2020", "第五百七十七条")], @@ -496,6 +497,11 @@ LEGAL_ARTICLE_REFERENCE_RULES = [ "triggers": ["电子签名", "签名", "盖章"], "targets": [("LAW-CIVIL-CODE-2020", "第四百九十条"), ("LAW-E-SIGNATURE-2019", "第十三条"), ("LAW-E-SIGNATURE-2019", "第十四条")], }, + { + "triggers": ["保密", "商业秘密"], + "targets": [("LAW-CIVIL-CODE-2020", "第五百零九条"), ("LAW-CIVIL-CODE-2020", "第五百七十七条")], + }, + # 个人信息与数据安全 { "triggers": ["个人信息", "隐私", "敏感个人信息", "告知同意"], "targets": [("LAW-PIPL-2021", "第十三条"), ("LAW-PIPL-2021", "第十四条"), ("LAW-PIPL-2021", "第十七条"), ("LAW-PIPL-2021", "第二十八条")], @@ -505,13 +511,148 @@ LEGAL_ARTICLE_REFERENCE_RULES = [ "targets": [("LAW-DATA-SECURITY-2021", "第二十七条"), ("LAW-DATA-SECURITY-2021", "第二十九条"), ("LAW-DATA-SECURITY-2021", "第三十一条")], }, { - "triggers": ["保密", "商业秘密"], - "targets": [("LAW-CIVIL-CODE-2020", "第五百零九条"), ("LAW-CIVIL-CODE-2020", "第五百七十七条")], + "triggers": ["数据出境", "跨境传输", "境外"], + "targets": [("LAW-DATA-EXPORT-SECURITY-2022", "第二条"), ("LAW-PI-EXPORT-CONTRACT-2023", "第二条"), ("LAW-DATA-SECURITY-2021", "第三十一条")], }, + # 劳动用工 { "triggers": ["竞业限制", "劳动合同", "服务期"], "targets": [("LAW-LABOR-CONTRACT-2012", "第二十三条"), ("LAW-LABOR-CONTRACT-2012", "第二十四条"), ("LAW-LABOR-CONTRACT-2012", "第二十二条")], }, + { + "triggers": ["试用期", "试用期限"], + "targets": [("LAW-LABOR-CONTRACT-2012", "第十九条"), ("LAW-LABOR-CONTRACT-2012", "第二十条")], + }, + { + "triggers": ["社会保险", "社保", "五险一金"], + "targets": [("LAW-SOCIAL-INSURANCE-2018", "第五十八条"), ("LAW-SOCIAL-INSURANCE-2018", "第六十条"), ("LAW-LABOR-CONTRACT-2012", "第三十八条")], + }, + { + "triggers": ["工伤", "职业病", "工伤认定"], + "targets": [("LAW-WORK-INJURY-2010", "第十四条"), ("LAW-WORK-INJURY-2010", "第十五条"), ("LAW-WORK-INJURY-2010", "第二十条")], + }, + { + "triggers": ["解除劳动合同", "经济补偿", "赔偿金"], + "targets": [("LAW-LABOR-CONTRACT-2012", "第三十九条"), ("LAW-LABOR-CONTRACT-2012", "第四十六条"), ("LAW-LABOR-CONTRACT-2012", "第四十七条")], + }, + # 公司商事 + { + "triggers": ["股权转让", "股东", "股权"], + "targets": [("LAW-COMPANY-2023", "第八十四条"), ("LAW-COMPANY-2023", "第八十五条"), ("LAW-COMPANY-2023", "第八十六条")], + }, + { + "triggers": ["股东会", "股东大会", "决议"], + "targets": [("LAW-COMPANY-2023", "第五十九条"), ("LAW-COMPANY-2023", "第六十条"), ("LAW-COMPANY-2023", "第六十一条")], + }, + { + "triggers": ["法定代表人", "董事", "监事"], + "targets": [("LAW-COMPANY-2023", "第十条"), ("LAW-COMPANY-2023", "第六十七条"), ("LAW-COMPANY-2023", "第七十六条")], + }, + { + "triggers": ["注册资本", "出资", "增资"], + "targets": [("LAW-COMPANY-2023", "第四十八条"), ("LAW-COMPANY-2023", "第四十九条"), ("LAW-COMPANY-2023", "第五十五条")], + }, + # 投融资 + { + "triggers": ["证券", "股票", "上市公司"], + "targets": [("LAW-SECURITIES-2019", "第四条"), ("LAW-SECURITIES-2019", "第五条"), ("LAW-SECURITIES-2019", "第十九条")], + }, + { + "triggers": ["反垄断", "垄断协议", "滥用市场支配地位"], + "targets": [("LAW-ANTI-MONOPOLY-2022", "第十三条"), ("LAW-ANTI-MONOPOLY-2022", "第二十二条"), ("LAW-ANTI-MONOPOLY-2022", "第二十三条")], + }, + { + "triggers": ["外商投资", "外资"], + "targets": [("LAW-FOREIGN-INVEST-2019", "第二条"), ("LAW-FOREIGN-INVEST-2019", "第三条"), ("LAW-FOREIGN-INVEST-2019", "第四条")], + }, + # 招投标采购 + { + "triggers": ["招投标", "投标", "中标"], + "targets": [("LAW-TENDER-2017", "第三条"), ("LAW-TENDER-2017", "第四条"), ("LAW-TENDER-2017", "第三十七条")], + }, + { + "triggers": ["政府采购", "采购"], + "targets": [("LAW-GOVERNMENT-PROCUREMENT-2014", "第二条"), ("LAW-GOVERNMENT-PROCUREMENT-2014", "第三条"), ("LAW-GOVERNMENT-PROCUREMENT-2014", "第四条")], + }, + # 建设工程 + { + "triggers": ["建设工程", "工程质量", "验收"], + "targets": [("LAW-CONSTRUCTION-2019", "第六十一条"), ("LAW-CONSTRUCTION-QUALITY-2019", "第三条"), ("LAW-CONSTRUCTION-QUALITY-2019", "第十六条")], + }, + { + "triggers": ["施工许可", "建筑许可"], + "targets": [("LAW-CONSTRUCTION-2019", "第七条"), ("LAW-CONSTRUCTION-2019", "第八条")], + }, + # 知识产权 + { + "triggers": ["著作权", "版权", "软件著作权"], + "targets": [("LAW-COPYRIGHT-2020", "第二条"), ("LAW-COPYRIGHT-2020", "第三条"), ("LAW-COPYRIGHT-2020", "第十条")], + }, + { + "triggers": ["专利", "专利权", "发明专利"], + "targets": [("LAW-PATENT-2020", "第二条"), ("LAW-PATENT-2020", "第十条"), ("LAW-PATENT-2020", "第十一条")], + }, + { + "triggers": ["商标", "商标权", "注册商标"], + "targets": [("LAW-TRADEMARK-2019", "第三条"), ("LAW-TRADEMARK-2019", "第四条"), ("LAW-TRADEMARK-2019", "第七条")], + }, + # 担保 + { + "triggers": ["担保", "保证", "抵押", "质押"], + "targets": [("LAW-CIVIL-CODE-2020", "第三百八十六条"), ("LAW-CIVIL-CODE-2020", "第三百八十七条"), ("LAW-CIVIL-CODE-GUARANTEE-INTERP-2020", "第一条")], + }, + # 借款 + { + "triggers": ["借款", "借贷", "利息"], + "targets": [("LAW-CIVIL-CODE-2020", "第六百六十七条"), ("LAW-CIVIL-CODE-2020", "第六百七十四条"), ("LAW-PRIVATE-LENDING-INTERP-2020", "第一条")], + }, + # 争议解决 + { + "triggers": ["仲裁", "仲裁协议"], + "targets": [("LAW-ARBITRATION-2017", "第四条"), ("LAW-ARBITRATION-2017", "第五条"), ("LAW-ARBITRATION-2017", "第六条")], + }, + { + "triggers": ["管辖", "诉讼", "法院"], + "targets": [("LAW-CIVIL-CODE-2020", "第四百六十五条"), ("LAW-CIVIL-CODE-CONTRACT-INTERP-2023", "第一条")], + }, + # 特许经营 + { + "triggers": ["特许经营", "加盟", "连锁"], + "targets": [("LAW-FRANCHISE-MGMT-2007", "第三条"), ("LAW-FRANCHISE-MGMT-2007", "第七条"), ("LAW-FRANCHISE-MGMT-2007", "第八条")], + }, + { + "triggers": ["信息披露", "特许经营信息"], + "targets": [("LAW-FRANCHISE-DISCLOSURE-2012", "第四条"), ("LAW-FRANCHISE-DISCLOSURE-2012", "第五条")], + }, + # 房地产 + { + "triggers": ["房地产", "土地使用权", "建设用地"], + "targets": [("LAW-REAL-ESTATE-MGMT-2019", "第二条"), ("LAW-LAND-MGMT-2019", "第二条"), ("LAW-LAND-MGMT-2019", "第九条")], + }, + { + "triggers": ["物业", "物业管理", "物业费"], + "targets": [("LAW-PROPERTY-MGMT-2018", "第二条"), ("LAW-PROPERTY-MGMT-2018", "第三十五条"), ("LAW-PROPERTY-MGMT-2018", "第三十六条")], + }, + # 技术合同 + { + "triggers": ["技术服务", "技术开发", "技术转让"], + "targets": [("LAW-CIVIL-CODE-2020", "第八百四十三条"), ("LAW-CIVIL-CODE-2020", "第八百四十五条"), ("LAW-TECH-CONTRACT-INTERP-2020", "第一条")], + }, + # 反不正当竞争 + { + "triggers": ["不正当竞争", "虚假宣传", "商业贿赂"], + "targets": [("LAW-ANTI-UNFAIR-COMP-2019", "第二条"), ("LAW-ANTI-UNFAIR-COMP-2019", "第八条"), ("LAW-ANTI-UNFAIR-COMP-2019", "第七条")], + }, + # 电子商务 + { + "triggers": ["电子商务", "平台", "网络交易"], + "targets": [("LAW-E-COMMERCE-2018", "第二条"), ("LAW-E-COMMERCE-2018", "第九条"), ("LAW-E-COMMERCE-2018", "第十条")], + }, + # 消费者权益 + { + "triggers": ["消费者", "消费者权益", "退货"], + "targets": [("LAW-CONSUMER-2013", "第二条"), ("LAW-CONSUMER-2013", "第二十四条"), ("LAW-CONSUMER-2013", "第二十五条")], + }, ] LEGAL_SOURCE_ALIAS_MAP = { "LAW-CIVIL-CODE-2020": ["民法典", "中华人民共和国民法典"], @@ -520,6 +661,35 @@ LEGAL_SOURCE_ALIAS_MAP = { "LAW-LABOR-CONTRACT-2012": ["劳动合同法", "中华人民共和国劳动合同法"], "LAW-DATA-SECURITY-2021": ["数据安全法", "中华人民共和国数据安全法"], "LAW-COMPANY-2023": ["公司法", "中华人民共和国公司法"], + "LAW-LABOR-2018": ["劳动法", "中华人民共和国劳动法"], + "LAW-CYBERSECURITY-2016": ["网络安全法", "中华人民共和国网络安全法"], + "LAW-PATENT-2020": ["专利法", "中华人民共和国专利法"], + "LAW-TRADEMARK-2019": ["商标法", "中华人民共和国商标法"], + "LAW-COPYRIGHT-2020": ["著作权法", "中华人民共和国著作权法"], + "LAW-ANTI-UNFAIR-COMP-2019": ["反不正当竞争法", "中华人民共和国反不正当竞争法"], + "LAW-ARBITRATION-2017": ["仲裁法", "中华人民共和国仲裁法"], + "LAW-E-COMMERCE-2018": ["电子商务法", "中华人民共和国电子商务法"], + "LAW-CONSUMER-2013": ["消费者权益保护法", "中华人民共和国消费者权益保护法"], + "LAW-FOREIGN-INVEST-2019": ["外商投资法", "中华人民共和国外商投资法"], + "LAW-ANTI-MONOPOLY-2022": ["反垄断法", "中华人民共和国反垄断法"], + "LAW-SECURITIES-2019": ["证券法", "中华人民共和国证券法"], + "LAW-TENDER-2017": ["招标投标法", "中华人民共和国招标投标法"], + "LAW-GOVERNMENT-PROCUREMENT-2014": ["政府采购法", "中华人民共和国政府采购法"], + "LAW-CONSTRUCTION-2019": ["建筑法", "中华人民共和国建筑法"], + "LAW-CONSTRUCTION-QUALITY-2019": ["建设工程质量管理条例"], + "LAW-SOCIAL-INSURANCE-2018": ["社会保险法", "中华人民共和国社会保险法"], + "LAW-WORK-INJURY-2010": ["工伤保险条例"], + "LAW-PROPERTY-MGMT-2018": ["物业管理条例"], + "LAW-LAND-MGMT-2019": ["土地管理法", "中华人民共和国土地管理法"], + "LAW-REAL-ESTATE-MGMT-2019": ["城市房地产管理法", "中华人民共和国城市房地产管理法"], + "LAW-FRANCHISE-MGMT-2007": ["商业特许经营管理条例"], + "LAW-FRANCHISE-DISCLOSURE-2012": ["商业特许经营信息披露管理办法"], + "LAW-PRIVATE-LENDING-INTERP-2020": ["民间借贷司法解释"], + "LAW-TECH-CONTRACT-INTERP-2020": ["技术合同纠纷司法解释"], + "LAW-CIVIL-CODE-GUARANTEE-INTERP-2020": ["担保制度解释", "民法典担保制度解释"], + "LAW-CIVIL-CODE-CONTRACT-INTERP-2023": ["合同编通则司法解释", "民法典合同编通则解释"], + "LAW-DATA-EXPORT-SECURITY-2022": ["数据出境安全评估办法"], + "LAW-PI-EXPORT-CONTRACT-2023": ["个人信息出境标准合同办法"], } diff --git a/backend/app/services/review_service.py b/backend/app/services/review_service.py index 07d820dd..dbc7f1c2 100644 --- a/backend/app/services/review_service.py +++ b/backend/app/services/review_service.py @@ -1458,6 +1458,22 @@ class ReviewService: limit=5, ) refs = [self._legal_article_ref_payload(article, match_reason="runtime_search") for article in articles] + # Semantic search fallback: when rule-based and legal_basis matching both fail, + # use vector semantic search to find relevant legal articles + if not refs and combined_text: + try: + from app.services.legal_article_service import LegalArticleService + legal_article_service = LegalArticleService(self.db) + query_text = (item_text + " " + source_text[:500]) if source_text else item_text + semantic_results = legal_article_service.search_runtime_sources( + {"tenant_id": tenant_id, "role": "system"}, + query_text, + limit=3, + ) + if semantic_results: + refs = [self._legal_article_ref_payload(r, match_reason="semantic_search") for r in semantic_results] + except Exception: + pass # Semantic search is best-effort; do not break review flow on failure next_item["legal_article_refs"] = refs enriched.append(next_item) return enriched diff --git a/frontend/src/components/legal/LegalArticlePreview.tsx b/frontend/src/components/legal/LegalArticlePreview.tsx new file mode 100644 index 00000000..121a1aa6 --- /dev/null +++ b/frontend/src/components/legal/LegalArticlePreview.tsx @@ -0,0 +1,217 @@ +"use client"; + +import { useState } from "react"; +import { Modal, Typography, Space, Tag, Button, Divider, Tooltip, message } from "antd"; +import { CopyOutlined, FileTextOutlined, LinkOutlined } from "@ant-design/icons"; +import Link from "next/link"; + +const { Text, Paragraph } = Typography; + +interface LegalArticleRef { + id: string; + source_id: string; + source_title: string; + article_no: string; + chapter_title?: string; + section_title?: string; + authority?: string; + effective_date?: string; + validity_status?: string; + region?: string; + tags?: string[]; + article_excerpt?: string; + article_text?: string; + knowledge_base_id?: string; + knowledge_document_id?: string; + detail_path?: string; + match_reason?: string; + updated_at?: string; +} + +interface LegalArticlePreviewProps { + ref: LegalArticleRef; + children?: React.ReactNode; + showMatchReason?: boolean; + compact?: boolean; +} + +/** + * 法条引用预览组件 + * + * 功能: + * 1. 悬停显示法条摘要 + * 2. 点击打开详情弹窗 + * 3. 支持一键复制法条文本 + * 4. 显示匹配原因 + */ +export default function LegalArticlePreview({ + ref: legalRef, + children, + showMatchReason = true, + compact = false, +}: LegalArticlePreviewProps) { + const [modalVisible, setModalVisible] = useState(false); + const [copied, setCopied] = useState(false); + + const handleCopy = async () => { + const text = [ + legalRef.source_title, + legalRef.article_no, + legalRef.article_text || legalRef.article_excerpt || "", + ] + .filter(Boolean) + .join("\n"); + + try { + await navigator.clipboard.writeText(text); + setCopied(true); + message.success("法条已复制到剪贴板"); + setTimeout(() => setCopied(false), 2000); + } catch { + message.error("复制失败"); + } + }; + + // 紧凑模式:只显示法条名称和条款号 + if (compact) { + return ( + <> + +
+ {legalRef.source_title} {legalRef.article_no} +
+
+ {(legalRef.article_text || legalRef.article_excerpt || "").slice(0, 150)} + ... +
+ + } + > + setModalVisible(true)} style={{ cursor: "pointer" }}> + {children || `${legalRef.source_title} ${legalRef.article_no}`} + +
+ + + + {legalRef.source_title} {legalRef.article_no} + + } + open={modalVisible} + onCancel={() => setModalVisible(false)} + footer={[ + , + legalRef.detail_path ? ( + + + + ) : null, + , + ]} + width={720} + > + + {/* 法条元数据 */} + + {legalRef.source_title} + {legalRef.article_no && {legalRef.article_no}} + {legalRef.authority && {legalRef.authority}} + {legalRef.validity_status && {legalRef.validity_status}} + {showMatchReason && legalRef.match_reason && ( + 匹配方式: {legalRef.match_reason} + )} + + + {/* 章节信息 */} + {(legalRef.chapter_title || legalRef.section_title) && ( +
+ {legalRef.section_title && ( + + 节:{legalRef.section_title} + + )} + {legalRef.chapter_title && ( + + 章:{legalRef.chapter_title} + + )} +
+ )} + + + + {/* 法条全文 */} + + {legalRef.article_text || legalRef.article_excerpt || "法条全文暂无"} + + + {/* 生效日期 */} + {legalRef.effective_date && ( + + 生效日期:{legalRef.effective_date} + + )} +
+
+ + ); + } + + // 完整模式:显示卡片式预览 + return ( +
+
+ + + {legalRef.source_title} + {legalRef.article_no && {legalRef.article_no}} + + {showMatchReason && legalRef.match_reason && ( + + 匹配方式: {legalRef.match_reason} + + )} + + + + {legalRef.detail_path && ( + + + + )} + +
+ + {/* 法条摘要 */} +
+ + {legalRef.article_text || legalRef.article_excerpt || "法条全文暂无"} + +
+
+ ); +} \ No newline at end of file