Quellcode durchsuchen

feat(时效性):添加时效性测试

lingmin_package@163.com vor 1 Woche
Ursprung
Commit
aa58c429df

+ 1 - 1
Dockerfile

@@ -49,7 +49,7 @@ ENV PATH="/venv/bin:$PATH"
 
 # 先复制 requirements 文件安装依赖(利用缓存)
 COPY requirements.txt /tmp/
-RUN /venv/bin/pip config set global.index-url https://mirrors.aliyun.com/pypi/simple \
+RUN /venv/bin/pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple \
     && /venv/bin/pip config set install.trusted-host mirrors.aliyun.com \
     && /venv/bin/pip --default-timeout=1800 install -r /tmp/requirements.txt \
     && rm -rf /root/.cache

+ 166 - 0
utils_test/standard_new_Test/README.md

@@ -0,0 +1,166 @@
+# 标准库匹配规则系统 - 内存处理版本
+
+基于 `standard_timeliness_new.md` 文档实现的标准库时效性审查系统,采用**内存数据处理**架构。
+
+## 架构设计
+
+```
+┌─────────────────────────────────────────────────────────────┐
+│                    StandardMatchingService                   │
+│                      (对外服务接口)                           │
+└───────────────────────┬─────────────────────────────────────┘
+                        │
+        ┌───────────────┴───────────────┐
+        │                               │
+┌───────▼────────┐            ┌────────▼────────┐
+│StandardRepository│          │ StandardMatcher │
+│   (数据仓库)    │            │   (匹配逻辑)     │
+└───────┬────────┘            └─────────────────┘
+        │
+┌───────▼────────┐
+│   MySQL DAO    │
+│  (一次性加载)   │
+└────────────────┘
+```
+
+### 核心组件
+
+| 组件 | 职责 | 说明 |
+|------|------|------|
+| `StandardRepository` | 内存数据存储和索引 | 加载数据后建立多索引加速查询 |
+| `StandardMatcher` | 匹配规则逻辑 | 纯内存匹配,无数据库访问 |
+| `StandardMatchingService` | 对外服务接口 | 统一入口,管理数据加载 |
+| `StandardDAO` | 数据库访问 | 只负责一次性加载所有数据 |
+
+## 文件结构
+
+```
+utils_test/standard_new_Test/
+├── standard_dao.py              # 数据访问层(一次性加载)
+├── standard_service.py          # 核心业务逻辑(内存处理)
+│   ├── StandardRepository       # 数据仓库类
+│   ├── StandardMatcher          # 匹配器类
+│   └── StandardMatchingService  # 服务类
+├── test_app.py                  # 实际调用测试
+├── test_standard_matching.py    # 单元测试
+└── README.md                    # 说明文档
+```
+
+## 标准库数据结构
+
+| 字段 | 说明 |
+|------|------|
+| id | 序号 |
+| standard_number | 标准号 |
+| standard_name (chinese_name) | 标准名称 |
+| validity | 时效性 (XH-现行, SX-试行, FZ-废止) |
+
+## 五种匹配结果状态
+
+| 情况 | 条件 | process_result | status_code | final_result |
+|------|------|----------------|-------------|--------------|
+| 1 | 名称+标准号都匹配,现行/试行 | 状态正常 | OK | 无问题 |
+| 2 | 名称+标准号都匹配,废止,有现行替代 | 状态被替代 | SUBSTITUTED | 《标准》(号)已废止,替代《标准》(号) |
+| 3 | 名称+标准号都匹配,废止,无替代 | 状态废止无现行 | ABOLISHED | 《标准》(号)已废止,无现行状态 |
+| 4 | 名称或标准号只有一个匹配 | 不匹配 | MISMATCH | 《提取》(号)与实际《标准》(号)不匹配 |
+| 5 | 都不匹配 | 标准库不存在 | NOT_FOUND | 《标准》(号)标准库不存在,请确认 |
+
+## 使用方法
+
+### 1. 初始化服务(一次性加载数据到内存)
+
+```python
+from utils_test.standard_new_Test.standard_service import StandardMatchingService
+
+# Mock 模式(无需数据库)
+service = StandardMatchingService(db_pool=None)
+await service.initialize()
+
+# 真实数据库模式
+from foundation.database.base.sql.async_mysql_conn_pool import AsyncMySQLPool
+db_pool = AsyncMySQLPool()
+await db_pool.initialize()
+service = StandardMatchingService(db_pool=db_pool)
+await service.initialize()  # 从MySQL加载所有数据到内存
+```
+
+### 2. 单个标准检查
+
+```python
+result = service.check_single(
+    seq_no=1,
+    standard_name="铁路桥涵设计规范",
+    standard_number="TB 10002-2017"
+)
+
+print(f"状态: {result.status_code}")  # OK
+print(f"结果: {result.final_result}")  # 无问题
+```
+
+### 3. 批量检查
+
+```python
+standards = [
+    {"standard_name": "铁路桥涵设计规范", "standard_number": "TB 10002-2017"},
+    {"standard_name": "缆索起重机", "standard_number": "GB/T 28756-2012"},
+]
+
+results = service.check_standards(standards)
+
+for r in results:
+    print(f"{r.seq_no}. {r.original_name} ({r.original_number})")
+    print(f"   状态: {r.status_code}")
+    print(f"   结果: {r.final_result}")
+    if r.substitute_number:
+        print(f"   替代: {r.substitute_name} ({r.substitute_number})")
+```
+
+## 运行测试
+
+```bash
+# 运行实际调用测试
+cd utils_test/standard_new_Test
+python test_app.py
+
+# 运行单元测试
+python -m pytest test_standard_matching.py -v
+```
+
+## 代码设计特点
+
+### 1. 内存数据处理
+- 服务初始化时一次性从MySQL加载所有数据到内存
+- 后续匹配操作完全在内存中进行,无数据库访问
+- 适合标准库数据量不大但需要高频查询的场景
+
+### 2. 可维护性优化
+- **分层架构**:Repository(数据)+ Matcher(逻辑)+ Service(接口)
+- **单一职责**:每个方法只做一件事,如 `_set_ok_result()`、`_handle_abolished()`
+- **索引优化**:标准号、名称多索引加速内存查询
+
+### 3. 匹配规则实现
+匹配流程拆分为多个独立方法:
+```
+match()
+├── _handle_number_matched()      # 标准号匹配
+│   ├── _handle_full_match()      # 名称也匹配
+│   │   ├── _set_ok_result()      # 现行/试行
+│   │   └── _handle_abolished()   # 废止
+│   └── _handle_name_mismatch()   # 名称不匹配
+└── _handle_number_not_matched()  # 标准号不匹配
+    ├── _check_name_in_records()
+    └── _search_by_name_only()
+```
+
+## 数据表结构
+
+```sql
+CREATE TABLE t_samp_standard_base_info (
+    id INT PRIMARY KEY AUTO_INCREMENT,
+    chinese_name VARCHAR(500) COMMENT '标准名称',
+    standard_number VARCHAR(100) COMMENT '标准编号',
+    validity VARCHAR(10) COMMENT '时效性 (XH-现行, SX-试行, FZ-废止)',
+    INDEX idx_number (standard_number),
+    INDEX idx_name (chinese_name)
+) COMMENT='标准库基础信息表';
+```

+ 255 - 0
utils_test/standard_new_Test/TEST_CASES.md

@@ -0,0 +1,255 @@
+# 标准库匹配规则测试案例文档
+
+> 根据 `standard_timeliness_new.md` 文档生成
+> 文件位置: `utils_test/standard_new_Test/test_standard_matching_rules.py`
+
+## 测试案例统计
+
+| 类别 | 测试数量 | 说明 |
+|------|---------|------|
+| 情况1-正常(OK) | 5 | 名称+标准号都匹配,现行/试行 |
+| 情况2-被替代(SUBSTITUTED) | 2 | 名称+标准号都匹配,废止,有现行替代 |
+| 情况3-废止无替代(ABOLISHED) | 3 | 名称+标准号都匹配,废止,无替代 |
+| 情况4-不匹配(MISMATCH) | 6 | 名称或标准号只有一个匹配,现行状态 |
+| 情况5-不存在(NOT_FOUND) | 3 | 名称和标准号都不匹配 |
+| 边界情况 | 5 | 特殊场景测试 |
+| 批量测试 | 1 | 混合场景批量验证 |
+| **总计** | **25** | |
+
+---
+
+## 详细测试案例
+
+### 情况1: 正常(OK)- 5个案例
+
+测试类: `TestCaseOK`
+
+| 编号 | 测试名称 | 输入标准名称 | 输入标准号 | 库中状态 | 预期结果 |
+|------|---------|-------------|-----------|---------|---------|
+| TC-OK-01 | 基本匹配 | 铁路桥涵设计规范 | TB 10002-2017 | XH | OK |
+| TC-OK-02 | 输入带书名号 | 《铁路桥涵设计规范》 | TB 10002-2017 | XH | OK |
+| TC-OK-03 | 试行状态 | 混凝土结构耐久性设计标准 | GB/T 50476-2019 | SX | OK |
+| TC-OK-04 | 带空格名称 | 铁路混凝土工程施工质量验收标准 | TB 10424-2018 | XH | OK |
+| TC-OK-05 | 库中带书名号 | 铁路工程抗震设计规范 | GB 50111-2006 | XH | OK |
+
+**验证规则**:
+- 名称和标准号完全匹配
+- 支持书名号变体(输入/库中带或不带)
+- 支持空格差异
+- 现行(XH)和试行(SX)都返回OK
+
+---
+
+### 情况2: 被替代(SUBSTITUTED)- 2个案例
+
+测试类: `TestCaseSubstituted`
+
+| 编号 | 测试名称 | 输入标准名称 | 输入标准号 | 库中状态 | 替代标准 | 预期结果 |
+|------|---------|-------------|-----------|---------|---------|---------|
+| TC-SUB-01 | 单条替代 | 起重机 钢丝绳 保养、维护、检验和报废 | GB/T 5972-2016 | FZ→XH | GB/T 5972-2023 | SUBSTITUTED |
+| TC-SUB-02 | 带书名号输入 | 《起重机 钢丝绳 保养、维护、检验和报废》 | GB/T 5972-2016 | FZ→XH | GB/T 5972-2023 | SUBSTITUTED |
+
+**验证规则**:
+- 输入标准已废止(FZ)
+- 库中有同名现行替代标准
+- 返回最新版本的替代标准信息
+- 消息包含"已废止"和"替代"关键词
+
+---
+
+### 情况3: 废止无替代(ABOLISHED)- 3个案例
+
+测试类: `TestCaseAbolished`
+
+| 编号 | 测试名称 | 输入标准名称 | 输入标准号 | 库中状态 | 是否有替代 | 预期结果 |
+|------|---------|-------------|-----------|---------|-----------|---------|
+| TC-ABOL-01 | 缆索起重机 | 缆索起重机 | GB/T 28756-2012 | FZ | 无 | ABOLISHED |
+| TC-ABOL-02 | 电力高处作业防坠器 | 电力高处作业防坠器 | DL/T 1147-2009 | FZ | 无 | ABOLISHED |
+| TC-ABOL-03 | 多版本废止 | 旧版施工规范 | TB 10001-2000 | FZ(多个) | 无 | ABOLISHED |
+
+**验证规则**:
+- 输入标准已废止(FZ)
+- 库中没有同名现行替代标准
+- 返回"废止无现行"状态
+- 消息包含"已废止,无现行状态"
+
+---
+
+### 情况4: 不匹配(MISMATCH)- 6个案例
+
+测试类: `TestCaseMismatch`
+
+| 编号 | 测试名称 | 输入标准名称 | 输入标准号 | 库中匹配记录 | 预期结果 |
+|------|---------|-------------|-----------|-------------|---------|
+| TC-MIS-01 | 年份不匹配 | 公路水运危险性较大工程专项施工方案编制审查规程 | JT/T 1495-2023 | 名称同, JT/T 1495-2024 XH | MISMATCH |
+| TC-MIS-02 | 带书名号年份不匹配 | 《公路水运危险性较大工程专项施工方案编制审查规程》 | JT/T 1495-2023 | 名称同, JT/T 1495-2024 XH | MISMATCH |
+| TC-MIS-03 | 名称匹配年份错误 | 铁路桥涵设计规范 | TB 10002-2020 | 名称同, TB 10002-2017 XH | MISMATCH |
+| TC-MIS-04 | 前缀匹配名称模糊 | 钢结构设计标准 | GB 50017-2010 | 钢结构设计标准, GB 50017-2017 XH | MISMATCH/NOT_FOUND |
+| TC-MIS-05 | 标准号匹配名称不匹配(现行) | 铁路桥涵设计 | TB 10002-2017 | 铁路桥涵设计规范, TB 10002-2017 XH | MISMATCH |
+| TC-MIS-06 | 标准号匹配名称不匹配(废止) | 某个错误名称 | GB/T 28756-2012 | 缆索起重机, GB/T 28756-2012 FZ | ABOLISHED |
+
+**验证规则**:
+- 标准号匹配但名称不匹配,或反之
+- 现行状态返回MISMATCH
+- 废止状态按废止逻辑处理(ABOLISHED或SUBSTITUTED)
+- 返回实际库中的标准信息
+
+---
+
+### 情况5: 不存在(NOT_FOUND)- 3个案例
+
+测试类: `TestCaseNotFound`
+
+| 编号 | 测试名称 | 输入标准名称 | 输入标准号 | 库中是否存在 | 预期结果 |
+|------|---------|-------------|-----------|-------------|---------|
+| TC-NF-01 | 完全不存在 | 完全不存在的标准名称 | GB/T 99999-9999 | 不存在 | NOT_FOUND |
+| TC-NF-02 | 格式正确但不存在 | 某个规范 | TB 88888-2099 | 不存在 | NOT_FOUND |
+| TC-NF-03 | 名称部分相似 | 桥涵设计 | TB 10002-2017 | 不匹配 | NOT_FOUND/MISMATCH |
+
+**验证规则**:
+- 名称和标准号都无法在库中找到匹配
+- 返回"标准库不存在"状态
+- 消息提示用户确认
+
+---
+
+### 边界情况 - 5个案例
+
+测试类: `TestCaseEdgeCases`
+
+| 编号 | 测试名称 | 输入 | 说明 | 预期结果 |
+|------|---------|------|------|---------|
+| TC-EDGE-01 | 多版本选择最新 | 多版本标准测试, GB/T 99999-2015 | 库中有2015(FZ), 2020(XH), 2023(XH) | SUBSTITUTED, 返回2023版 |
+| TC-EDGE-02 | 空标准号 | 无标准号规范, "" | 仅按名称查询 | MISMATCH/NOT_FOUND |
+| TC-EDGE-03 | 特殊字符 | 《特殊(字符)》规范, Q/CR 9001-2020 | 名称含特殊字符 | OK |
+| TC-EDGE-04 | 全角空格 | 铁路桥涵 设计规范, TB 10002-2017 | 名称含全角空格 | OK |
+| TC-EDGE-05 | 空名称 | "", TB 10002-2017 | 名称为空字符串 | NOT_FOUND/MISMATCH |
+
+---
+
+### 批量测试 - 1个案例
+
+测试类: `TestCaseBatch`
+
+| 编号 | 测试名称 | 输入列表 | 预期结果 |
+|------|---------|---------|---------|
+| TC-BATCH-01 | 混合标准批量测试 | [OK, SUBSTITUTED, ABOLISHED, MISMATCH, NOT_FOUND] | 按顺序返回对应状态码 |
+
+**验证规则**:
+- 批量处理多个标准
+- 每个标准返回对应的状态
+- 保持输入顺序
+
+---
+
+## 测试数据构造
+
+### Mock数据定义
+
+```python
+def build_mock_data():
+    return [
+        # 情况1: 正常
+        {"id": 1, "standard_name": "铁路桥涵设计规范", "standard_number": "TB 10002-2017", "validity": "XH"},
+        {"id": 2, "standard_name": "《铁路工程抗震设计规范》", "standard_number": "GB 50111-2006", "validity": "XH"},
+        {"id": 3, "standard_name": "混凝土结构耐久性设计标准", "standard_number": "GB/T 50476-2019", "validity": "SX"},
+        {"id": 4, "standard_name": "铁路混凝土工程施工质量验收标准", "standard_number": "TB 10424-2018", "validity": "XH"},
+
+        # 情况2: 被替代
+        {"id": 5, "standard_name": "起重机 钢丝绳 保养、维护、检验和报废", "standard_number": "GB/T 5972-2016", "validity": "FZ"},
+        {"id": 6, "standard_name": "起重机 钢丝绳 保养、维护、检验和报废", "standard_number": "GB/T 5972-2023", "validity": "XH"},
+
+        # 情况3: 废止无替代
+        {"id": 7, "standard_name": "缆索起重机", "standard_number": "GB/T 28756-2012", "validity": "FZ"},
+        {"id": 8, "standard_name": "电力高处作业防坠器", "standard_number": "DL/T 1147-2009", "validity": "FZ"},
+        {"id": 9, "standard_name": "旧版施工规范", "standard_number": "TB 10001-2000", "validity": "FZ"},
+        {"id": 10, "standard_name": "旧版施工规范", "standard_number": "TB 10001-2005", "validity": "FZ"},
+
+        # 情况4: 不匹配
+        {"id": 11, "standard_name": "公路水运危险性较大工程专项施工方案编制审查规程", "standard_number": "JT/T 1495-2024", "validity": "XH"},
+        {"id": 12, "standard_name": "建筑地基基础设计规范", "standard_number": "GB 50007-2011", "validity": "XH"},
+        {"id": 13, "standard_name": "钢结构设计标准", "standard_number": "GB 50017-2017", "validity": "XH"},
+
+        # 边界: 多版本
+        {"id": 14, "standard_name": "多版本标准测试", "standard_number": "GB/T 99999-2015", "validity": "FZ"},
+        {"id": 15, "standard_name": "多版本标准测试", "standard_number": "GB/T 99999-2020", "validity": "XH"},
+        {"id": 16, "standard_name": "多版本标准测试", "standard_number": "GB/T 99999-2023", "validity": "XH"},
+
+        # 边界: 其他
+        {"id": 17, "standard_name": "无标准号规范", "standard_number": "DB 51/T 9999-2020", "validity": "XH"},
+        {"id": 18, "standard_name": "《特殊(字符)》规范", "standard_number": "Q/CR 9001-2020", "validity": "XH"},
+    ]
+```
+
+---
+
+## 运行测试
+
+```bash
+# 运行所有测试
+cd utils_test/standard_new_Test
+python -m pytest test_standard_matching_rules.py -v
+
+# 运行特定类别的测试
+python -m pytest test_standard_matching_rules.py::TestCaseOK -v
+python -m pytest test_standard_matching_rules.py::TestCaseSubstituted -v
+python -m pytest test_standard_matching_rules.py::TestCaseMismatch -v
+
+# 生成HTML报告
+python -m pytest test_standard_matching_rules.py --html=report.html
+```
+
+---
+
+## 状态码说明
+
+| 状态码 | 说明 | 消息格式 |
+|--------|------|---------|
+| OK | 正常 | 无问题 |
+| SUBSTITUTED | 被替代 | 《标准》(号)已废止,替代《标准》(号) |
+| ABOLISHED | 废止无现行 | 《标准》(号)已废止,无现行状态 |
+| MISMATCH | 不匹配 | 《提取》(号)与 实际《标准》(号)不匹配 |
+| NOT_FOUND | 不存在 | 《标准》(号)标准库不存在,请确认 |
+
+---
+
+## 匹配规则流程图(文字版)
+
+```
+开始
+│
+├─► 标准号精确匹配?
+│   │
+│   ├─► 是 ► 名称匹配?
+│   │   │
+│   │   ├─► 是 ► 时效性检查
+│   │   │   │
+│   │   │   ├─► 现行/试行 ► OK
+│   │   │   └─► 废止 ► 查找替代 ► 有则SUBSTITUTED, 无则ABOLISHED
+│   │   │
+│   │   └─► 否 ► 名称模糊匹配?
+│   │       │
+│   │       ├─► 是 ► 时效性检查 ► MISMATCH(现行) 或 废止处理
+│   │       └─► 否 ► NOT_FOUND
+│   │
+│   └─► 否 ► 标准号模糊匹配?
+│       │
+│       ├─► 是 ► 名称匹配?
+│       │   │
+│       │   ├─► 是 ► 时效性检查 ► MISMATCH(现行) 或 废止处理
+│       │   └─► 否 ► NOT_FOUND
+│       │
+│       └─► 否 ► 名称模糊匹配?
+│           │
+│           ├─► 是 ► 名称精确匹配?
+│           │   │
+│           │   ├─► 是 ► 时效性检查 ► MISMATCH(现行) 或 NOT_FOUND(废止)
+│           │   └─► 否 ► NOT_FOUND
+│           │
+│           └─► 否 ► NOT_FOUND
+```
+
+---
+
+*文档生成时间: 2026-03-30*
+*配套代码: test_standard_matching_rules.py*

+ 49 - 0
utils_test/standard_new_Test/standard_dao.py

@@ -0,0 +1,49 @@
+"""
+标准库数据访问对象
+用于从MySQL一次性加载所有标准数据到内存
+"""
+import sys
+import os
+# 添加项目根目录到 Python 路径
+current_dir = os.path.dirname(os.path.abspath(__file__))
+project_root = os.path.dirname(os.path.dirname(current_dir))
+if project_root not in sys.path:
+    sys.path.insert(0, project_root)
+
+from typing import List, Dict, Optional
+
+
+class StandardDAO:
+    """标准库数据访问对象 - 负责从数据库加载数据"""
+
+    def __init__(self, db_pool):
+        self.db_pool = db_pool
+        self.table_name = "t_samp_standard_base_info"
+
+    async def load_all_standards(self) -> List[Dict]:
+        """
+        一次性从MySQL加载所有标准数据到内存
+
+        Returns:
+            标准列表,每个标准包含:
+                - id: 序号
+                - standard_name: 标准名称(chinese_name)
+                - standard_number: 标准号
+                - validity: 时效性(XH/SX/FZ)
+        """
+        query = f"""
+            SELECT
+                id,
+                chinese_name AS standard_name,
+                standard_number,
+                validity
+            FROM {self.table_name}
+        """
+        try:
+            async with self.db_pool.get_cursor() as cursor:
+                await cursor.execute(query)
+                results = await cursor.fetchall()
+                return [dict(row) for row in results] if results else []
+        except Exception as e:
+            print(f"加载标准库数据失败: {e}")
+            raise

+ 674 - 0
utils_test/standard_new_Test/standard_service.py

@@ -0,0 +1,674 @@
+"""
+标准库匹配规则服务 - 内存处理版本
+实现施工方案审查-时效性审查的匹配逻辑
+
+架构:
+- StandardRepository: 内存数据存储和索引
+- StandardMatcher: 匹配规则逻辑
+- StandardMatchingService: 对外服务接口
+"""
+import sys
+import os
+# 添加项目根目录到 Python 路径
+current_dir = os.path.dirname(os.path.abspath(__file__))
+project_root = os.path.dirname(os.path.dirname(current_dir))
+if project_root not in sys.path:
+    sys.path.insert(0, project_root)
+
+from typing import List, Dict, Optional, Set
+from dataclasses import dataclass, field
+from enum import Enum
+
+
+class ValidityStatus(Enum):
+    """时效性状态"""
+    CURRENT = "XH"      # 现行
+    TRIAL = "SX"        # 试行
+    ABOLISHED = "FZ"    # 废止
+
+
+class MatchResultCode(Enum):
+    """匹配结果状态码"""
+    OK = "OK"                       # 正常
+    SUBSTITUTED = "SUBSTITUTED"     # 被替代
+    ABOLISHED = "ABOLISHED"         # 废止无现行
+    MISMATCH = "MISMATCH"           # 不匹配
+    NOT_FOUND = "NOT_FOUND"         # 标准库不存在
+
+
+@dataclass
+class StandardMatchResult:
+    """标准匹配结果数据结构"""
+    seq_no: int = 0                             # 序号
+    original_name: str = ""                      # 原始标准名称
+    original_number: str = ""                    # 原始标准号
+    substitute_number: Optional[str] = None      # 替代标准号(如果有)
+    substitute_name: Optional[str] = None        # 替代标准名称(如果有)
+    process_result: str = ""                     # 处理结果状态
+    status_code: str = ""                        # 状态码
+    final_result: str = ""                       # 最终结果消息
+
+
+@dataclass
+class StandardRecord:
+    """标准记录数据结构"""
+    id: int
+    standard_name: str
+    standard_number: str
+    validity: str
+
+
+class StandardRepository:
+    """
+    标准库内存数据仓库
+    负责加载和索引标准数据,支持快速查询
+    """
+
+    def __init__(self):
+        # 原始数据列表
+        self._records: List[StandardRecord] = []
+
+        # 索引结构,加速查询
+        self._number_index: Dict[str, StandardRecord] = {}  # 标准号 -> 记录
+        self._name_index: Dict[str, List[StandardRecord]] = {}  # 名称 -> 记录列表
+        self._current_records: List[StandardRecord] = []  # 现行/试行标准列表
+
+    def load_data(self, raw_data: List[Dict]):
+        """
+        加载原始数据到内存并建立索引
+
+        Args:
+            raw_data: 从数据库查询的原始标准数据列表
+        """
+        self._records = []
+        self._number_index = {}
+        self._name_index = {}
+        self._current_records = []
+
+        for item in raw_data:
+            # 跳过无效数据
+            standard_number = item.get("standard_number")
+            standard_name = item.get("standard_name")
+            if not standard_number or not standard_name:
+                continue
+
+            record = StandardRecord(
+                id=item.get("id", 0),
+                standard_name=standard_name,
+                standard_number=standard_number,
+                validity=item.get("validity", "")
+            )
+            self._records.append(record)
+
+            # 建立标准号索引
+            self._number_index[record.standard_number] = record
+
+            # 建立名称索引(一个名称可能对应多个标准号)
+            if record.standard_name not in self._name_index:
+                self._name_index[record.standard_name] = []
+            self._name_index[record.standard_name].append(record)
+
+            # 收集现行/试行标准
+            if record.validity in [ValidityStatus.CURRENT.value, ValidityStatus.TRIAL.value]:
+                self._current_records.append(record)
+
+        # 对现行标准按标准号降序排序(用于找最新替代标准)
+        # 处理可能的 None 值
+        self._current_records.sort(
+            key=lambda r: r.standard_number or "",
+            reverse=True
+        )
+        print(f"self._records={len(self._records)}")
+
+    def find_by_number_exact(self, standard_number: str) -> Optional[StandardRecord]:
+        """精确匹配标准号"""
+        return self._number_index.get(standard_number)
+
+    def find_by_name_exact(self, standard_name: str) -> Optional[StandardRecord]:
+        """精确匹配标准名称(返回第一个)"""
+        records = self._name_index.get(standard_name, [])
+        return records[0] if records else None
+
+    def find_by_name_fuzzy(self, standard_name: str) -> List[StandardRecord]:
+        """模糊匹配标准名称"""
+        results = []
+        for name, records in self._name_index.items():
+            if standard_name in name or name in standard_name:
+                results.extend(records)
+        return results
+
+    def find_by_number_fuzzy(self, standard_number: str) -> List[StandardRecord]:
+        """模糊匹配标准号"""
+        results = []
+        # 提取前缀(如 GB/T 5972)
+        parts = standard_number.split("-")
+        prefix = parts[0] if parts else standard_number
+
+        for number, record in self._number_index.items():
+            # 前缀匹配
+            if number.startswith(prefix):
+                results.append(record)
+        return results
+
+    def find_current_by_name(self, standard_name: str) -> List[StandardRecord]:
+        """查询指定名称的现行/试行标准(支持模糊匹配)"""
+        results = []
+        for record in self._current_records:
+            # 精确匹配
+            if record.standard_name == standard_name:
+                results.append(record)
+            # 模糊匹配(忽略空格、书名号等)
+            elif self._is_name_fuzzy_match_for_repo(record.standard_name, standard_name):
+                results.append(record)
+        return results
+
+    def _is_name_fuzzy_match_for_repo(self, name1: str, name2: str) -> bool:
+        """判断两个标准名称是否模糊匹配"""
+        clean1 = name1.replace("《", "").replace("》", "").replace(" ", "").replace(" ", "")
+        clean2 = name2.replace("《", "").replace("》", "").replace(" ", "").replace(" ", "")
+        return clean1 == clean2
+
+    def get_all_records(self) -> List[StandardRecord]:
+        """获取所有记录"""
+        return self._records.copy()
+
+
+class StandardMatcher:
+    """
+    标准匹配器
+    实现标准库匹配规则的核心逻辑
+    """
+
+    def __init__(self, repository: StandardRepository):
+        self.repo = repository
+
+    def match(self, seq_no: int, input_name: str, input_number: str) -> StandardMatchResult:
+        """
+        执行标准匹配
+
+        匹配流程:
+        1. 标准号精确匹配
+        2. 根据匹配结果进入不同分支处理
+        """
+        # 去除前后空格
+        input_name = input_name.strip() if input_name else input_name
+        input_number = input_number.strip() if input_number else input_number
+
+        # 清洗书名号和括号
+        input_name = self._clean_brackets_and_booknames(input_name)
+        input_number = self._clean_brackets_and_booknames(input_number)
+
+        result = StandardMatchResult(
+            seq_no=seq_no,
+            original_name=input_name,
+            original_number=input_number
+        )
+
+        # 步骤1: 精确匹配标准号
+        match_by_number = self.repo.find_by_number_exact(input_number)
+
+        if match_by_number:
+            # 分支A: 标准号匹配成功
+            return self._handle_number_matched(result, match_by_number, input_name)
+        else:
+            # 分支B: 标准号未匹配
+            return self._handle_number_not_matched(result, input_name, input_number)
+
+    def _handle_number_matched(
+        self,
+        result: StandardMatchResult,
+        db_record: StandardRecord,
+        input_name: str
+    ) -> StandardMatchResult:
+        """处理标准号匹配成功的情况"""
+        # 检查名称是否匹配
+        if db_record.standard_name == input_name:
+            # 名称也匹配
+            return self._handle_full_match(result, db_record)
+        else:
+            # 名称不匹配
+            return self._handle_name_mismatch(result, db_record, input_name)
+
+    def _handle_full_match(
+        self,
+        result: StandardMatchResult,
+        db_record: StandardRecord
+    ) -> StandardMatchResult:
+        """处理名称和标准号都完全匹配的情况"""
+        if db_record.validity in [ValidityStatus.CURRENT.value, ValidityStatus.TRIAL.value]:
+            # 情况1: 现行或试行 - 状态正常
+            return self._set_ok_result(result)
+        else:
+            # 废止状态 - 查找替代标准
+            return self._handle_abolished(result, db_record)
+
+    def _handle_name_mismatch(
+        self,
+        result: StandardMatchResult,
+        db_record: StandardRecord,
+        input_name: str
+    ) -> StandardMatchResult:
+        """处理标准号匹配但名称不匹配的情况"""
+        # 首先检查是否是名称模糊匹配(忽略空格、书名号等)
+        if self._is_name_fuzzy_match(db_record.standard_name, input_name):
+            # 名称模糊匹配成功,按完全匹配处理
+            return self._handle_full_match(result, db_record)
+
+        # 尝试用输入的名称模糊匹配
+        name_matches = self.repo.find_by_name_fuzzy(input_name)
+
+        # 查找精确名称匹配
+        exact_match = self._find_exact_name_match(name_matches, input_name)
+
+        if exact_match:
+            # 找到名称匹配的记录
+            return self._handle_fuzzy_name_match(result, exact_match)
+
+        # 尝试在模糊匹配结果中查找模糊名称匹配
+        for match in name_matches:
+            if self._is_name_fuzzy_match(match.standard_name, input_name):
+                return self._handle_fuzzy_name_match(result, match)
+
+        # 名称完全不匹配,但标准号已匹配成功
+        # 说明该标准存在于库中,应返回不匹配而非不存在
+        if db_record.validity in [ValidityStatus.CURRENT.value, ValidityStatus.TRIAL.value]:
+            return self._set_mismatch_result(result, db_record)
+        elif db_record.validity == ValidityStatus.ABOLISHED.value:
+            return self._handle_abolished(result, db_record)
+
+        return self._set_not_found_result(result)
+
+    def _handle_number_not_matched(
+        self,
+        result: StandardMatchResult,
+        input_name: str,
+        input_number: str
+    ) -> StandardMatchResult:
+        """处理标准号未匹配的情况"""
+        # 尝试模糊匹配标准号
+        fuzzy_number_matches = self.repo.find_by_number_fuzzy(input_number)
+
+        if fuzzy_number_matches:
+            # 检查名称是否匹配
+            return self._check_name_in_records(result, fuzzy_number_matches, input_name)
+        else:
+            # 尝试直接按名称查询
+            return self._search_by_name_only(result, input_name)
+
+    def _check_name_in_records(
+        self,
+        result: StandardMatchResult,
+        records: List[StandardRecord],
+        input_name: str
+    ) -> StandardMatchResult:
+        """在一批记录中查找名称匹配"""
+        # 首先尝试精确匹配
+        for record in records:
+            if record.standard_name == input_name:
+                # 名称匹配,检查状态
+                if record.validity in [ValidityStatus.CURRENT.value, ValidityStatus.TRIAL.value]:
+                    return self._set_mismatch_result(result, record)
+                elif record.validity == ValidityStatus.ABOLISHED.value:
+                    return self._handle_abolished(result, record)
+
+        # 尝试模糊名称匹配(忽略空格和书名号)
+        for record in records:
+            if self._is_name_fuzzy_match(record.standard_name, input_name):
+                # 名称模糊匹配成功
+                if record.validity in [ValidityStatus.CURRENT.value, ValidityStatus.TRIAL.value]:
+                    return self._set_mismatch_result(result, record)
+                elif record.validity == ValidityStatus.ABOLISHED.value:
+                    return self._handle_abolished(result, record)
+
+        # 名称不匹配
+        return self._set_not_found_result(result)
+
+    def _search_by_name_only(
+        self,
+        result: StandardMatchResult,
+        input_name: str
+    ) -> StandardMatchResult:
+        """仅通过名称查询"""
+        # 精确匹配名称
+        name_match = self.repo.find_by_name_exact(input_name)
+
+        if name_match:
+            if name_match.validity in [ValidityStatus.CURRENT.value, ValidityStatus.TRIAL.value]:
+                return self._set_mismatch_result(result, name_match)
+            elif name_match.validity == ValidityStatus.ABOLISHED.value:
+                return self._set_not_found_result(result)
+
+        # 模糊匹配名称
+        fuzzy_matches = self.repo.find_by_name_fuzzy(input_name)
+
+        # 首先尝试精确匹配
+        exact_match = self._find_exact_name_match(fuzzy_matches, input_name)
+        if exact_match:
+            if exact_match.validity in [ValidityStatus.CURRENT.value, ValidityStatus.TRIAL.value]:
+                return self._set_mismatch_result(result, exact_match)
+
+        # 尝试模糊名称匹配(忽略空格、书名号等)
+        for match in fuzzy_matches:
+            if self._is_name_fuzzy_match(match.standard_name, input_name):
+                if match.validity in [ValidityStatus.CURRENT.value, ValidityStatus.TRIAL.value]:
+                    return self._set_mismatch_result(result, match)
+                elif match.validity == ValidityStatus.ABOLISHED.value:
+                    return self._handle_abolished(result, match)
+
+        return self._set_not_found_result(result)
+
+    def _handle_fuzzy_name_match(
+        self,
+        result: StandardMatchResult,
+        match_record: StandardRecord
+    ) -> StandardMatchResult:
+        """处理模糊名称匹配成功的情况"""
+        if match_record.validity in [ValidityStatus.CURRENT.value, ValidityStatus.TRIAL.value]:
+            return self._set_mismatch_result(result, match_record)
+        elif match_record.validity == ValidityStatus.ABOLISHED.value:
+            return self._handle_abolished(result, match_record)
+        return self._set_not_found_result(result)
+
+    def _handle_abolished(
+        self,
+        result: StandardMatchResult,
+        abolished_record: StandardRecord
+    ) -> StandardMatchResult:
+        """处理已废止标准的情况"""
+        # 查询同名现行标准作为替代
+        substitutes = self.repo.find_current_by_name(abolished_record.standard_name)
+
+        if substitutes:
+            # 有替代标准,取最新的(已按标准号降序)
+            latest = substitutes[0]
+            return self._set_substituted_result(result, latest)
+        else:
+            # 无替代标准
+            return self._set_abolished_result(result)
+
+    # ========== 格式化方法 ==========
+
+    def _format_standard_name(self, name: str) -> str:
+        """格式化标准名称,确保只有一个《》包裹"""
+        if not name:
+            return name
+        name = name.strip()
+        # 去除已有的书名号
+        while name.startswith('《'):
+            name = name[1:]
+        while name.endswith('》'):
+            name = name[:-1]
+        return f"《{name}》"
+
+    def _format_standard_number(self, number: str) -> str:
+        """格式化标准编号,确保用()包裹"""
+        if not number:
+            return number
+        number = number.strip()
+        # 去除已有的括号
+        if number.startswith('(') or number.startswith('('):
+            number = number[1:]
+        if number.endswith(')') or number.endswith(')'):
+            number = number[:-1]
+        return f"({number})"
+
+    # ========== 结果设置方法(每个方法职责单一) ==========
+
+    def _set_ok_result(self, result: StandardMatchResult) -> StandardMatchResult:
+        """设置状态正常的结果"""
+        result.process_result = "正常"
+        result.status_code = MatchResultCode.OK.value
+        result.final_result = "无问题"
+        return result
+
+    def _set_substituted_result(
+        self,
+        result: StandardMatchResult,
+        substitute: StandardRecord
+    ) -> StandardMatchResult:
+        """设置被替代的结果"""
+        result.substitute_name = self._format_standard_name(substitute.standard_name)
+        result.substitute_number = self._format_standard_number(substitute.standard_number)
+        result.process_result = "被替代"
+        result.status_code = MatchResultCode.SUBSTITUTED.value
+        result.final_result = (
+            f"{self._format_standard_name(result.original_name)}"
+            f"{self._format_standard_number(result.original_number)}已废止,"
+            f"替代{self._format_standard_name(substitute.standard_name)}"
+            f"{self._format_standard_number(substitute.standard_number)}"
+        )
+        return result
+
+    def _set_abolished_result(self, result: StandardMatchResult) -> StandardMatchResult:
+        """设置废止无替代的结果"""
+        result.process_result = "废止无现行"
+        result.status_code = MatchResultCode.ABOLISHED.value
+        result.final_result = (
+            f"{self._format_standard_name(result.original_name)}"
+            f"{self._format_standard_number(result.original_number)}已废止,无现行状态"
+        )
+        return result
+
+    def _set_mismatch_result(
+        self,
+        result: StandardMatchResult,
+        actual: StandardRecord
+    ) -> StandardMatchResult:
+        """设置不匹配的结果"""
+        result.substitute_name = self._format_standard_name(actual.standard_name)
+        result.substitute_number = self._format_standard_number(actual.standard_number)
+        result.process_result = "不匹配"
+        result.status_code = MatchResultCode.MISMATCH.value
+        result.final_result = (
+            f"{self._format_standard_name(result.original_name)}"
+            f"{self._format_standard_number(result.original_number)}"
+            f"与实际{self._format_standard_name(actual.standard_name)}"
+            f"{self._format_standard_number(actual.standard_number)}不匹配"
+        )
+        return result
+
+    def _set_not_found_result(self, result: StandardMatchResult) -> StandardMatchResult:
+        """设置不存在的结果"""
+        result.process_result = "标准库不存在"
+        result.status_code = MatchResultCode.NOT_FOUND.value
+        result.final_result = (
+            f"{self._format_standard_name(result.original_name)}"
+            f"{self._format_standard_number(result.original_number)}标准库不存在,请确认"
+        )
+        return result
+
+    # ========== 工具方法 ==========
+
+    def _is_name_fuzzy_match(self, name1: str, name2: str) -> bool:
+        """
+        判断两个标准名称是否模糊匹配
+        只去除书名号,保留中间空格(中间空格属于名称的一部分)
+        """
+        # 清理书名号,但保留中间空格
+        clean1 = name1.replace("《", "").replace("》", "")
+        clean2 = name2.replace("《", "").replace("》", "")
+        return clean1 == clean2
+
+    def _clean_brackets_and_booknames(self, text: str) -> str:
+        """
+        清洗字符串前后的书名号和括号
+        包括:《》()()
+        """
+        if not text:
+            return text
+
+        # 循环去除前后的书名号和括号,直到没有变化
+        changed = True
+        while changed:
+            changed = False
+            original = text
+
+            # 去除前导的书名号和括号
+            if text.startswith("《"):
+                text = text[1:]
+                changed = True
+            if text.startswith("》"):
+                text = text[1:]
+                changed = True
+            if text.startswith("("):
+                text = text[1:]
+                changed = True
+            if text.startswith(")"):
+                text = text[1:]
+                changed = True
+            if text.startswith("("):
+                text = text[1:]
+                changed = True
+            if text.startswith(")"):
+                text = text[1:]
+                changed = True
+
+            # 去除尾随的书名号和括号
+            if text.endswith("《"):
+                text = text[:-1]
+                changed = True
+            if text.endswith("》"):
+                text = text[:-1]
+                changed = True
+            if text.endswith("("):
+                text = text[:-1]
+                changed = True
+            if text.endswith(")"):
+                text = text[:-1]
+                changed = True
+            if text.endswith("("):
+                text = text[:-1]
+                changed = True
+            if text.endswith(")"):
+                text = text[:-1]
+                changed = True
+
+            # 如果文本变空了,停止循环
+            if not text:
+                break
+
+        return text
+
+    def _find_exact_name_match(
+        self,
+        records: List[StandardRecord],
+        target_name: str
+    ) -> Optional[StandardRecord]:
+        """在记录列表中查找精确名称匹配"""
+        for record in records:
+            if record.standard_name == target_name:
+                return record
+        return None
+
+
+class StandardMatchingService:
+    """
+    标准库匹配服务
+    对外暴露的统一接口
+    """
+
+    def __init__(self, db_pool=None):
+        """
+        初始化服务
+
+        Args:
+            db_pool: 数据库连接池,如果为None则使用Mock数据
+        """
+        self.db_pool = db_pool
+        self.repository = StandardRepository()
+        self.matcher = StandardMatcher(self.repository)
+        self._initialized = False
+
+    async def initialize(self):
+        """
+        初始化:从数据库加载数据到内存
+        只需要执行一次
+        """
+        if self._initialized:
+            return
+
+        if self.db_pool:
+            # 从真实数据库加载
+            from utils_test.standard_new_Test.standard_dao import StandardDAO
+            dao = StandardDAO(self.db_pool)
+            raw_data = await dao.load_all_standards()
+            print(f"raw_data={len(raw_data)}")
+        else:
+            # 使用Mock数据
+            raw_data = self._get_mock_data()
+
+        self.repository.load_data(raw_data)
+        self._initialized = True
+
+    async def close(self):
+        """关闭服务,清理资源"""
+        if self.db_pool:
+            await self.db_pool.close()
+        self._initialized = False
+
+    def check_standards(self, standards: List[Dict[str, str]]) -> List[StandardMatchResult]:
+        """
+        批量检查标准列表
+
+        Args:
+            standards: 标准列表,每个元素包含:
+                - standard_name: 标准名称(原始)
+                - standard_number: 标准号(原始)
+
+        Returns:
+            List[StandardMatchResult]: 匹配结果列表
+        """
+        if not self._initialized:
+            raise RuntimeError("服务未初始化,请先调用 initialize()")
+
+        results = []
+        for idx, std in enumerate(standards, start=1):
+            result = self.matcher.match(
+                seq_no=idx,
+                input_name=std.get("standard_name", ""),
+                input_number=std.get("standard_number", "")
+            )
+            results.append(result)
+        return results
+
+    def check_single(
+        self,
+        seq_no: int,
+        standard_name: str,
+        standard_number: str
+    ) -> StandardMatchResult:
+        """
+        检查单个标准
+
+        Args:
+            seq_no: 序号
+            standard_name: 标准名称
+            standard_number: 标准号
+
+        Returns:
+            StandardMatchResult: 匹配结果
+        """
+        if not self._initialized:
+            raise RuntimeError("服务未初始化,请先调用 initialize()")
+
+        return self.matcher.match(seq_no, standard_name, standard_number)
+
+    def _get_mock_data(self) -> List[Dict]:
+        """获取Mock数据 - 文档中的7个测试案例"""
+        return [
+            # 情况1: 正常现行标准
+            {"id": 1, "standard_name": "铁路桥涵设计规范", "standard_number": "TB 10002-2017", "validity": "XH"},
+            {"id": 2, "standard_name": "铁路工程抗震设计规范", "standard_number": "GB 50111-2006", "validity": "XH"},
+            {"id": 3, "standard_name": "铁路混凝土工程施工质量验收标准", "standard_number": "TB 10424-2018", "validity": "XH"},
+
+            # 情况4: 不匹配(年份错误)- 输入2023,实际2024
+            {"id": 4, "standard_name": "公路水运危险性较大工程专项施工方案编制审查规程", "standard_number": "JT/T 1495-2024", "validity": "XH"},
+
+            # 情况2: 被替代(废止+有现行替代)
+            {"id": 5, "standard_name": "起重机 钢丝绳 保养、维护、检验和报废", "standard_number": "GB/T 5972-2016", "validity": "FZ"},
+            {"id": 6, "standard_name": "起重机 钢丝绳 保养、维护、检验和报废", "standard_number": "GB/T 5972-2023", "validity": "XH"},
+
+            # 情况3: 废止无替代
+            {"id": 7, "standard_name": "缆索起重机", "standard_number": "GB/T 28756-2012", "validity": "FZ"},
+            {"id": 8, "standard_name": "电力高处作业防坠器", "standard_number": "DL/T 1147-2009", "validity": "FZ"},
+        ]

+ 163 - 0
utils_test/standard_new_Test/standard_timeliness_new.md

@@ -0,0 +1,163 @@
+conda activate lq_agent
+
+conda activate lq_agent
+
+### 施工方案审查-时效性审查设计
+   - 1、标准库数据结构 
+      - 序号
+      - 标准号 
+      - 标准名称
+      - 时效性 (XH  现行、试行  SX、 废止 FZ)
+  
+    - 2、标准数据来源
+      - 数据库查询(MySQL),可以一次查询所有 或 筛选
+      - 通过搜索引擎查询,再让大模型整理为标准库数据结构
+  
+    - 3、根据数据结构制定查询规则
+      - 查询规则
+        - 处理流程图规则 ,参考【标准库匹配规则】
+      - 查询入参结构定义 list
+        - 标准名称 (原始)
+        - 标准号 (原始)
+      - 查询返回结构定义
+       - 输出结构说明 list
+        - 序号
+        - 标准名称(原始的)
+        - 标准号(原始的)
+        - 替代标准号(如果有)
+        - 替代标准名称(如果有)
+        - 处理结果状态(五种情况:参考 查询处理状态定义)
+        - 处理结果消息提示:(五种情况:参考 查询处理状态定义)
+      - 查询处理状态定义
+         - 标准名称和标准号的匹配情况,现行状态 ,状态正常, 提示 "正常" 
+         - 标准名称和标准号的匹配情况,废止状态,有现行状态 , 状态被替代 , 提示 "《标准名称》(标准号)已废止,替代《标准名称》(标准号)"
+         - 标准名称和标准号的匹配情况,废止状态,无现行状态 , 状态废止无现行  , 提示 "《标准名称》(标准号)已废止,无现行状态"
+         - 标准名称 或 标准号 其中一个匹配情况,现行状态 , 状态不匹配 , 提示 "《标准名称》(标准号) 与 实际《标准名称》(标准号)不匹配"
+         - 标准名称 或 标准号 都不匹配 , 状态不存在, 提示 "《标准名称》(标准号)标准库不存在"
+  
+          ┌──────┬─────────────────────────────────────┬────────────────┬─────────────┬───────────────────────────────────────────┐
+          │ 情况 │                条件                  │ process_result │ status_code │               final_result                │
+          ├──────┼─────────────────────────────────────┼────────────────┼─────────────┼───────────────────────────────────────────┤
+          │ 1    │ 名称+标准号都匹配,现行/试行          │ 状态正常        │ OK          │ 无问题                                     │
+          ├──────┼─────────────────────────────────────┼────────────────┼─────────────┼───────────────────────────────────────────┤
+          │ 2    │ 名称+标准号都匹配,废止,有现行替代    │ 状态被替代      │ SUBSTITUTED │ 《标准》(号)已废止,替代《标准》(号)      │
+          ├──────┼─────────────────────────────────────┼────────────────┼─────────────┼───────────────────────────────────────────┤
+          │ 3    │ 名称+标准号都匹配,废止,无替代        │ 状态废止无现行  │ ABOLISHED   │ 《标准》(号)已废止,无现行状态             │
+          ├──────┼─────────────────────────────────────┼────────────────┼─────────────┼───────────────────────────────────────────┤
+          │ 4    │ 名称或标准号只有一个匹配              │ 不匹配          │ MISMATCH    │ 《提取》(号)与 实际《标准》(号)不匹配    │
+          ├──────┼─────────────────────────────────────┼────────────────┼─────────────┼───────────────────────────────────────────┤
+          │ 5    │ 都不匹配                             │ 标准库不存在    │ NOT_FOUND   │ 《标准》(号)标准库不存在,请确认           │
+          └──────┴─────────────────────────────────────┴────────────────┴─────────────┴───────────────────────────────────────────┘
+
+    - 4、
+  
+#### 标准库匹配规则
+  **1. 开始**
+  *   **动作**:查询标准库信息
+  *   **判断**:标准号全匹配?
+
+      *   **分支 A:存在 (Yes)**
+          *   **判断**:名称全匹配?
+              *   **分支 A1:存在 (Yes)**
+                  *   **判断**:验证时效性?
+                      *   **情况 1**:现行、试行 -> **结果**:返回正常
+                      *   **情况 2**:废止
+                          *   **动作**:根据名称查询最新标准(返回可能多条)
+                          *   **动作**:遍历每个最新标准记录
+                              *   **判断**:是否存在?
+                                  *   **情况 1**:不存在 -> **结果**:返回废止,无现行
+                                  *   **情况 2**:存在
+                                      *   **判断**:验证时效性?
+                                          *   **情况 1**:废止都没有找到 -> **结果**:返回废止,无现行
+                                          *   **情况 2**:现行、试行 -> **结果**:被替代,新的标准号、标准名称
+              *   **分支 A2:不存在 (No)**
+                  *   **判断**:名称模糊匹配?
+                      *   **情况 1**:不匹配 -> **结果**:标准号与名称不存在
+                      *   **情况 2**:匹配
+                          *   **判断**:验证时效性?
+                              *   **情况 1**:现行、试行 -> **结果**:标准号与名称不匹配,返回最新标准号与名称
+                              *   **情况 2**:废止 -> **动作**:查询被替代子流程
+
+      *   **分支 B:不存在 (No)**
+          *   **判断**:标准号模糊查询?
+              *   **分支 B1:存在 (Yes)**
+                  *   **判断**:名称全匹配?
+                      *   **情况 1**:不匹配 -> **结果**:标准号与名称不存在
+                      *   **情况 2**:匹配
+                          *   **判断**:验证时效性?
+                              *   **情况 1**:现行、试行 -> **结果**:标准号与名称不匹配,返回最新标准号与名称
+                              *   **情况 2**:废止 -> **动作**:查询被替代子流程
+              *   **分支 B2:不存在 (No)**
+                  *   **判断**:名称模糊查询?
+                      *   **情况 1**:不存在 -> **结果**:返回不存在
+                      *   **情况 2**:存在
+                          *   **判断**:名称全匹配?
+                              *   **情况 1**:不匹配 -> **结果**:标准号与名称不存在
+                              *   **情况 2**:匹配
+                                  *   **判断**:验证时效性?
+                                      *   **情况 1**:现行、试行 -> **结果**:标准号与名称不匹配(名称匹配,标准不匹配)
+                                      *   **情况 2**:废止 -> **结果**:标准号与名称不存在
+
+
+
+#### 废止数据
+  《电力高处作业防坠器》(DL/T 1147-2009)      FZ
+  《缆索起重机》(GB/T 28756-2012)							FZ
+
+#### 被替代标准名称和标准号的逻辑说明
+  - 1、首先根据标准名称和标准号查询状态,如果是废止状态,则根据标准名称查询状态为现行的,则此条标准名称和标准号为被替代标准名称和标准号,并返回被替代标准名称和标准号
+  - 2、根据标准名称查询时 返回多条标准名称和标准号,再代码里面进行逻辑判断
+  - 3、根据标准号查询时,返回标准名称,再根据标准名称查询 返回多条标准名称和标准号,再代码里面进行逻辑判断
+  - 4、类似如下数据:
+      《起重机 钢丝绳 保养、维护、检验和报废》GB/T 5972-2016				   FZ
+      《起重机 钢丝绳 保养、维护、检验和报废》GB/T 5972-2023					 XZ
+
+
+#### 目前测试的编制依据状态
+
+    (1)《铁路桥涵设计规范》 (TB 10002-2017)                  正常 
+    (2)《铁路工程抗震设计规范》 (GB 50111-2006)              正常 
+    (3)《铁路混凝土工程施工质量验收标准》 (TB 10424-2018)     正常 
+    (4)《公路水运危险性较大工程专项施工方案编制审查规程》(JT/T 1495-2023) 不匹配 《公路水运危险性较大工程专项施工方案编制审查规程》(JT/T 1495-2024)
+    (5)《起重机 钢丝绳 保养、维护、检验和报废》(GB/T 5972-2016)  被替代  《起重机 钢丝绳 保养、维护、检验和报废》(GB/T 5972-2023)
+    (6)《缆索起重机》(GB/T 28756-2012)	                    废止
+    (7)《电力高处作业防坠器》(DL/T 1147-2009)                废止
+
+
+
+
+
+
+#### 实现逻辑
+  - 1、查询数据源,目前从MySQL数据库一次查询到所有数据 保存到【标准库数据结构】
+    - select id  , chinese_name , standard_number , validity  t_samp_standard_base_info
+    - sql说明 chinese_name 标准名称, standard_number 标准编号, validity 时效性 (XH  现行、试行  SX、 废止 FZ)
+    - 
+
+  - 2、目前主程序中公共组件
+      - 2.1、代码先临时生成到 utils_test/standard_new_Test目录下,先做单元测试
+        - dao 数据库操作类
+        - service 业务操作
+        - test 单元测试
+      - 2.2、Mysql 查询公共组件 foundation.database.base.sql.async_mysql_conn_pool.py  公共继承 foundation.database.base.sql.async_mysql_base_dao.py AsyncBaseDAO
+
+
+
+
+#### 修正目前的问题
+   - 1、标准库数据结构 
+      - 序号
+      - 标准号 
+      - 标准名称
+      - 时效性 (XH  现行、试行  SX、 废止 FZ)
+  
+    - 2、标准数据来源
+      - 数据库查询(MySQL),可以一次查询所有 或 筛选
+
+    - 3、标准库数据处理在内存中初始化数据结构,通过MySQL一次查询所有数据,然后进行数据处理,处理结果保存在内存中,然后进行数据查询。
+    - 4、标准库匹配规则 是通过代码匹配标准库数据结构来实现的,而非通过数据库查询来实现,请调整代码逻辑,注意标准库匹配规则的逻辑实现 可维护性,不要在一个函数中写太多的逻辑代码
+
+
+ -- 存在问题的记录	
+	DL/T1147-2018  电力高处作业防坠器    DL/T 1147-2018  《电力高处作业防坠器》
+	DL/T1147-2009	《电力高处作业防坠器》  DL/T 1147-2009	《电力高处作业防坠器》

+ 221 - 0
utils_test/standard_new_Test/test_app.py

@@ -0,0 +1,221 @@
+"""
+标准库匹配规则 - 实际调用测试
+展示如何实际调用 StandardMatchingService 进行标准检查
+
+使用说明:
+1. MOCK_MODE = True:  使用模拟数据,无需数据库
+2. MOCK_MODE = False: 使用真实数据库,需要确保 config/config.ini 配置正确
+"""
+
+import sys
+import os
+
+# ==================== 配置区域 ====================
+# 设置为 True 使用 Mock 模式(无需数据库),False 使用真实数据库
+MOCK_MODE = False
+
+# ==================== 路径设置 ====================
+CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
+PROJECT_ROOT = os.path.dirname(os.path.dirname(CURRENT_DIR))
+sys.path.insert(0, PROJECT_ROOT)
+
+# 真实模式下需要修改工作目录以读取配置文件
+if not MOCK_MODE:
+    os.chdir(PROJECT_ROOT)
+
+import asyncio
+import json
+from utils_test.standard_new_Test.standard_service import (
+    StandardMatchingService,
+    MatchResultCode
+)
+
+
+# ==================== 服务创建 ====================
+
+async def create_service():
+    """创建并初始化服务实例"""
+    if MOCK_MODE:
+        print("[Mock 模式] 使用模拟数据,无需数据库连接")
+        service = StandardMatchingService(db_pool=None)
+        await service.initialize()
+        return service
+    else:
+        print("[真实模式] 连接数据库并加载标准数据...")
+        # 延迟导入,避免 Mock 模式下也需要安装依赖
+        from foundation.database.base.sql.async_mysql_conn_pool import AsyncMySQLPool
+        db_pool = AsyncMySQLPool()
+        await db_pool.initialize()
+
+        service = StandardMatchingService(db_pool=db_pool)
+        await service.initialize()
+        return service
+
+
+# ==================== 测试函数 ====================
+
+async def test_single_standard(service: StandardMatchingService):
+    """测试单个标准检查"""
+    print("\n" + "=" * 60)
+    print("【测试1】单个标准检查")
+    print("=" * 60)
+
+    result = service.check_single(
+        seq_no=1,
+        standard_name="铁路桥涵设计规范",
+        standard_number="TB 10002-2017"
+    )
+
+    print(f"\n序号: {result.seq_no}")
+    print(f"原始标准: 《{result.original_name}》({result.original_number})")
+    print(f"处理结果: {result.process_result}")
+    print(f"状态码: {result.status_code}")
+    print(f"结果消息: {result.final_result}")
+    if result.substitute_number:
+        print(f"替代标准: 《{result.substitute_name}》({result.substitute_number})")
+
+
+async def test_batch_check(service: StandardMatchingService):
+    """测试批量标准检查 - 文档中的7个示例"""
+    print("\n" + "=" * 60)
+    print("【测试2】批量标准检查 - 文档中的7个示例")
+    print("=" * 60)
+
+    standards = [
+        {"standard_name": "铁路桥涵设计规范", "standard_number": "TB 10002-2017"},
+        {"standard_name": "铁路工程抗震设计规范", "standard_number": "GB 50111-2006"},
+        {"standard_name": "铁路混凝土工程施工质量验收标准", "standard_number": "TB 10424-2018"},
+        {"standard_name": "公路水运危险性较大工程专项施工方案编制审查规程", "standard_number": "JT/T 1495-2023"},
+        {"standard_name": "起重机 钢丝绳 保养、维护、检验和报废", "standard_number": "GB/T 5972-2016"},
+        {"standard_name": "缆索起重机", "standard_number": "GB/T 28756-2012"},
+        {"standard_name": "电力高处作业防坠器", "standard_number": "DL/T 1147-2009"},
+
+
+    #    {"standard_name": "铁路桥涵设计", "standard_number": "TB 10002-2017"},
+    #    {"standard_name": "铁路桥涵设计规范", "standard_number": "TB 10002"},
+    #    {"standard_name": "铁路桥涵设计", "standard_number": "TB 10002"},
+    #    {"standard_name": "《铁路桥涵设计规范》", "standard_number": " (TB 10002-2017)"},
+    #     {"standard_name": "《铁路桥涵设计规范》", "standard_number": "(TB 10002-2017)"},
+    #    {"standard_name": " 铁路桥涵设计规范 ", "standard_number": "  TB 10002-2017 "},
+    #    {"standard_name": "铁路桥涵 设计规范", "standard_number": "TB 10002  -2017"},
+    #     {"standard_name": "铁路桥涵设计规范", "standard_number": "TB 10002  -2017"},
+    #      {"standard_name": "铁路桥涵 设计规范", "standard_number": "TB 10002-2017"},
+    ]
+
+    results = service.check_standards(standards)
+
+    print("\n检查结果汇总:")
+    print("-" * 80)
+    print(f"{'序号':<4} {'标准名称':<40} {'标准号':<20} {'状态':<12}")
+    print("-" * 80)
+
+    for r in results:
+        print(f"{r.seq_no:<4} {r.original_name:<40} {r.original_number:<20} {r.status_code:<12}")
+
+    print("\n" + "=" * 80)
+    print("详细结果:")
+    print("=" * 80)
+
+    for r in results:
+        print(f"\n【{r.seq_no}】{r.original_name}")
+        print(f"    标准号: {r.original_number}")
+        print(f"    状态: {r.process_result} ({r.status_code})")
+        print(f"    结果: {r.final_result}")
+        if r.substitute_number:
+            print(f"    ↳ 替代: 《{r.substitute_name}》({r.substitute_number})")
+
+
+async def test_various_cases(service: StandardMatchingService):
+    """测试各种边界情况"""
+    print("\n" + "=" * 60)
+    print("【测试3】各种边界情况测试")
+    print("=" * 60)
+
+    test_cases = [
+        {"name": "测试现行标准", "standard_name": "铁路桥涵设计规范", "standard_number": "TB 10002-2017"},
+        {"name": "测试被替代", "standard_name": "起重机 钢丝绳 保养、维护、检验和报废", "standard_number": "GB/T 5972-2016"},
+        {"name": "测试废止无替代", "standard_name": "缆索起重机", "standard_number": "GB/T 28756-2012"},
+        {"name": "测试不匹配", "standard_name": "公路水运危险性较大工程专项施工方案编制审查规程", "standard_number": "JT/T 1495-2023"},
+        {"name": "测试不存在", "standard_name": "不存在的标准", "standard_number": "GB/T 99999-9999"},
+    ]
+
+    for case in test_cases:
+        result = service.check_single(
+            seq_no=1,
+            standard_name=case["standard_name"],
+            standard_number=case["standard_number"]
+        )
+        print(f"\n--- {case['name']} ---")
+        print(f"输入: 《{result.original_name}》({result.original_number})")
+        print(f"输出: [{result.status_code}] {result.final_result}")
+
+
+async def export_results_to_json(service: StandardMatchingService, filename: str = "standard_check_results.json"):
+    """导出检查结果为JSON文件"""
+    print("\n" + "=" * 60)
+    print("【测试4】导出结果为JSON")
+    print("=" * 60)
+
+    standards = [
+        {"standard_name": "铁路桥涵设计规范", "standard_number": "TB 10002-2017"},
+        {"standard_name": "铁路混凝土工程施工质量验收标准", "standard_number": "TB 10424-2018"},
+        {"standard_name": "公路水运危险性较大工程专项施工方案编制审查规程", "standard_number": "JT/T 1495-2023"},
+        {"standard_name": "起重机 钢丝绳 保养、维护、检验和报废", "standard_number": "GB/T 5972-2016"},
+    ]
+
+    results = service.check_standards(standards)
+
+    output = []
+    for r in results:
+        output.append({
+            "seq_no": r.seq_no,
+            "original_name": r.original_name,
+            "original_number": r.original_number,
+            "substitute_name": r.substitute_name,
+            "substitute_number": r.substitute_number,
+            "process_result": r.process_result,
+            "status_code": r.status_code,
+            "final_result": r.final_result
+        })
+
+    with open(filename, "w", encoding="utf-8") as f:
+        json.dump(output, f, ensure_ascii=False, indent=2)
+
+    print(f"\n结果已导出到: {filename}")
+    print(json.dumps(output, ensure_ascii=False, indent=2))
+
+
+# ==================== 主函数 ====================
+
+async def main():
+    """主函数"""
+    print("=" * 80)
+    print("标准库匹配规则 - 实际调用测试(内存处理版本)")
+    print("=" * 80)
+    print(f"\n当前模式: {'Mock 测试模式(无需数据库)' if MOCK_MODE else '真实数据库模式'}")
+    print("-" * 80)
+
+    service = None
+    try:
+        # 创建并初始化服务
+        service = await create_service()
+        print(f"服务初始化成功! 数据已加载到内存\n")
+
+        # 执行测试
+        await test_single_standard(service)
+        await test_batch_check(service)
+        await test_various_cases(service)
+        # await export_results_to_json(service)  # 取消注释以测试JSON导出
+
+        print("\n" + "=" * 80)
+        print("所有测试执行完毕!")
+        print("=" * 80)
+
+    except Exception as e:
+        print(f"\n错误: {e}")
+        import traceback
+        traceback.print_exc()
+
+
+if __name__ == "__main__":
+    asyncio.run(main())

+ 207 - 0
utils_test/standard_new_Test/test_standard_matching.py

@@ -0,0 +1,207 @@
+"""
+标准库匹配规则单元测试 - 内存处理版本
+测试文档中定义的5种匹配情况
+"""
+import sys
+import os
+# 添加项目根目录到 Python 路径
+current_dir = os.path.dirname(os.path.abspath(__file__))
+project_root = os.path.dirname(os.path.dirname(current_dir))
+if project_root not in sys.path:
+    sys.path.insert(0, project_root)
+
+import pytest
+import asyncio
+from utils_test.standard_new_Test.standard_service import (
+    StandardRepository,
+    StandardMatcher,
+    StandardMatchingService,
+    MatchResultCode,
+    ValidityStatus,
+    StandardRecord
+)
+
+
+@pytest.fixture
+def mock_repository():
+    """创建模拟的数据仓库"""
+    repo = StandardRepository()
+    mock_data = [
+        # 情况1: 正常现行标准
+        {"id": 1, "standard_name": "铁路桥涵设计规范", "standard_number": "TB 10002-2017", "validity": "XH"},
+        {"id": 2, "standard_name": "铁路工程抗震设计规范", "standard_number": "GB 50111-2006", "validity": "XH"},
+        {"id": 3, "standard_name": "铁路混凝土工程施工质量验收标准", "standard_number": "TB 10424-2018", "validity": "XH"},
+
+        # 情况4: 不匹配(年份错误)- 输入2023,实际2024
+        {"id": 4, "standard_name": "公路水运危险性较大工程专项施工方案编制审查规程", "standard_number": "JT/T 1495-2024", "validity": "XH"},
+
+        # 情况2: 被替代(废止+有现行替代)
+        {"id": 5, "standard_name": "起重机 钢丝绳 保养、维护、检验和报废", "standard_number": "GB/T 5972-2016", "validity": "FZ"},
+        {"id": 6, "standard_name": "起重机 钢丝绳 保养、维护、检验和报废", "standard_number": "GB/T 5972-2023", "validity": "XH"},
+
+        # 情况3: 废止无替代
+        {"id": 7, "standard_name": "缆索起重机", "standard_number": "GB/T 28756-2012", "validity": "FZ"},
+        {"id": 8, "standard_name": "电力高处作业防坠器", "standard_number": "DL/T 1147-2009", "validity": "FZ"},
+    ]
+    repo.load_data(mock_data)
+    return repo
+
+
+@pytest.fixture
+def matcher(mock_repository):
+    """创建匹配器实例"""
+    return StandardMatcher(mock_repository)
+
+
+@pytest.fixture
+def mock_service():
+    """创建使用Mock数据的服务"""
+    service = StandardMatchingService(db_pool=None)
+    # 同步初始化(测试时使用event loop)
+    loop = asyncio.get_event_loop()
+    loop.run_until_complete(service.initialize())
+    return service
+
+
+class TestStandardRepository:
+    """测试数据仓库"""
+
+    def test_load_data(self, mock_repository):
+        """测试数据加载"""
+        assert len(mock_repository.get_all_records()) == 8
+
+    def test_find_by_number_exact(self, mock_repository):
+        """测试精确匹配标准号"""
+        result = mock_repository.find_by_number_exact("TB 10002-2017")
+        assert result is not None
+        assert result.standard_name == "铁路桥涵设计规范"
+
+    def test_find_by_name_exact(self, mock_repository):
+        """测试精确匹配名称"""
+        result = mock_repository.find_by_name_exact("铁路桥涵设计规范")
+        assert result is not None
+        assert result.standard_number == "TB 10002-2017"
+
+    def test_find_current_by_name(self, mock_repository):
+        """测试查询现行标准"""
+        results = mock_repository.find_current_by_name("起重机 钢丝绳 保养、维护、检验和报废")
+        assert len(results) == 1
+        assert results[0].standard_number == "GB/T 5972-2023"
+
+
+class TestStandardMatcher:
+    """测试匹配器"""
+
+    def test_case1_ok(self, matcher):
+        """情况1: 状态正常"""
+        result = matcher.match(1, "铁路桥涵设计规范", "TB 10002-2017")
+        assert result.status_code == MatchResultCode.OK.value
+        assert result.final_result == "无问题"
+
+    def test_case2_substituted(self, matcher):
+        """情况2: 被替代"""
+        result = matcher.match(1, "起重机 钢丝绳 保养、维护、检验和报废", "GB/T 5972-2016")
+        assert result.status_code == MatchResultCode.SUBSTITUTED.value
+        assert result.substitute_number == "GB/T 5972-2023"
+        assert "已废止" in result.final_result
+
+    def test_case3_abolished(self, matcher):
+        """情况3: 废止无替代"""
+        result = matcher.match(1, "缆索起重机", "GB/T 28756-2012")
+        assert result.status_code == MatchResultCode.ABOLISHED.value
+        assert "无现行状态" in result.final_result
+
+    def test_case4_mismatch(self, matcher):
+        """情况4: 不匹配"""
+        result = matcher.match(1, "公路水运危险性较大工程专项施工方案编制审查规程", "JT/T 1495-2023")
+        assert result.status_code == MatchResultCode.MISMATCH.value
+        assert result.substitute_number == "JT/T 1495-2024"
+
+    def test_case5_not_found(self, matcher):
+        """情况5: 不存在"""
+        result = matcher.match(1, "不存在的标准", "GB/T 99999-9999")
+        assert result.status_code == MatchResultCode.NOT_FOUND.value
+
+
+class TestStandardMatchingService:
+    """测试服务类"""
+
+    def test_check_single(self, mock_service):
+        """测试单个检查"""
+        result = mock_service.check_single(1, "铁路桥涵设计规范", "TB 10002-2017")
+        assert result.status_code == MatchResultCode.OK.value
+
+    def test_check_standards_batch(self, mock_service):
+        """测试批量检查"""
+        standards = [
+            {"standard_name": "铁路桥涵设计规范", "standard_number": "TB 10002-2017"},
+            {"standard_name": "缆索起重机", "standard_number": "GB/T 28756-2012"},
+            {"standard_name": "不存在的标准", "standard_number": "GB/T 99999-9999"},
+        ]
+        results = mock_service.check_standards(standards)
+        assert len(results) == 3
+        assert results[0].status_code == MatchResultCode.OK.value
+        assert results[1].status_code == MatchResultCode.ABOLISHED.value
+        assert results[2].status_code == MatchResultCode.NOT_FOUND.value
+
+
+class TestDocumentExamples:
+    """测试文档中列出的7个示例"""
+
+    @pytest.fixture
+    def doc_matcher(self):
+        """文档测试专用匹配器"""
+        repo = StandardRepository()
+        mock_data = [
+            {"id": 1, "standard_name": "铁路桥涵设计规范", "standard_number": "TB 10002-2017", "validity": "XH"},
+            {"id": 2, "standard_name": "铁路工程抗震设计规范", "standard_number": "GB 50111-2006", "validity": "XH"},
+            {"id": 3, "standard_name": "铁路混凝土工程施工质量验收标准", "standard_number": "TB 10424-2018", "validity": "XH"},
+            {"id": 4, "standard_name": "公路水运危险性较大工程专项施工方案编制审查规程", "standard_number": "JT/T 1495-2024", "validity": "XH"},
+            {"id": 5, "standard_name": "起重机 钢丝绳 保养、维护、检验和报废", "standard_number": "GB/T 5972-2016", "validity": "FZ"},
+            {"id": 6, "standard_name": "起重机 钢丝绳 保养、维护、检验和报废", "standard_number": "GB/T 5972-2023", "validity": "XH"},
+            {"id": 7, "standard_name": "缆索起重机", "standard_number": "GB/T 28756-2012", "validity": "FZ"},
+            {"id": 8, "standard_name": "电力高处作业防坠器", "standard_number": "DL/T 1147-2009", "validity": "FZ"},
+        ]
+        repo.load_data(mock_data)
+        return StandardMatcher(repo)
+
+    def test_example_1_railway_bridge(self, doc_matcher):
+        """(1)《铁路桥涵设计规范》(TB 10002-2017) - 正常"""
+        result = doc_matcher.match(1, "铁路桥涵设计规范", "TB 10002-2017")
+        assert result.status_code == MatchResultCode.OK.value
+
+    def test_example_2_seismic_design(self, doc_matcher):
+        """(2)《铁路工程抗震设计规范》(GB 50111-2006) - 正常"""
+        result = doc_matcher.match(1, "铁路工程抗震设计规范", "GB 50111-2006")
+        assert result.status_code == MatchResultCode.OK.value
+
+    def test_example_3_concrete(self, doc_matcher):
+        """(3)《铁路混凝土工程施工质量验收标准》(TB 10424-2018) - 正常"""
+        result = doc_matcher.match(1, "铁路混凝土工程施工质量验收标准", "TB 10424-2018")
+        assert result.status_code == MatchResultCode.OK.value
+
+    def test_example_4_highway_mismatch(self, doc_matcher):
+        """(4)年份不匹配 - 应为2024"""
+        result = doc_matcher.match(1, "公路水运危险性较大工程专项施工方案编制审查规程", "JT/T 1495-2023")
+        assert result.status_code == MatchResultCode.MISMATCH.value
+        assert result.substitute_number == "JT/T 1495-2024"
+
+    def test_example_5_crane_wire_substituted(self, doc_matcher):
+        """(5)被替代 GB/T 5972-2016 -> GB/T 5972-2023"""
+        result = doc_matcher.match(1, "起重机 钢丝绳 保养、维护、检验和报废", "GB/T 5972-2016")
+        assert result.status_code == MatchResultCode.SUBSTITUTED.value
+        assert result.substitute_number == "GB/T 5972-2023"
+
+    def test_example_6_cable_crane_abolished(self, doc_matcher):
+        """(6)废止无替代"""
+        result = doc_matcher.match(1, "缆索起重机", "GB/T 28756-2012")
+        assert result.status_code == MatchResultCode.ABOLISHED.value
+
+    def test_example_7_safety_device_abolished(self, doc_matcher):
+        """(7)废止无替代"""
+        result = doc_matcher.match(1, "电力高处作业防坠器", "DL/T 1147-2009")
+        assert result.status_code == MatchResultCode.ABOLISHED.value
+
+
+if __name__ == "__main__":
+    pytest.main([__file__, "-v"])

+ 417 - 0
utils_test/standard_new_Test/test_standard_matching_rules.py

@@ -0,0 +1,417 @@
+"""
+标准库匹配规则测试案例
+根据 standard_timeliness_new.md 文档中的匹配规则生成
+
+测试案例编号规则:
+- TC-OK-XX: 正常情况
+- TC-SUB-XX: 被替代情况
+- TC-ABOL-XX: 废止无替代情况
+- TC-MIS-XX: 不匹配情况
+- TC-NF-XX: 不存在情况
+"""
+
+import sys
+import os
+import pytest
+import asyncio
+
+# 添加项目根目录到 Python 路径
+current_dir = os.path.dirname(os.path.abspath(__file__))
+project_root = os.path.dirname(os.path.dirname(current_dir))
+if project_root not in sys.path:
+    sys.path.insert(0, project_root)
+
+from utils_test.standard_new_Test.standard_service import (
+    StandardRepository,
+    StandardMatcher,
+    StandardMatchingService,
+    MatchResultCode,
+    ValidityStatus,
+    StandardRecord
+)
+
+
+# ========================================
+# 测试数据构造
+# ========================================
+
+def build_mock_data():
+    """
+    构建测试数据集,覆盖所有匹配规则分支
+    """
+    return [
+        # ========== 情况1: 名称+标准号都匹配,现行/试行 ==========
+        # TC-OK-01: 标准格式
+        {"id": 1, "standard_name": "铁路桥涵设计规范", "standard_number": "TB 10002-2017", "validity": "XH"},
+        # TC-OK-02: 带书名号存储(测试名称清洗)
+        {"id": 2, "standard_name": "《铁路工程抗震设计规范》", "standard_number": "GB 50111-2006", "validity": "XH"},
+        # TC-OK-03: 试行状态
+        {"id": 3, "standard_name": "混凝土结构耐久性设计标准", "standard_number": "GB/T 50476-2019", "validity": "SX"},
+        # TC-OK-04: 带空格名称
+        {"id": 4, "standard_name": "铁路混凝土工程施工质量验收标准", "standard_number": "TB 10424-2018", "validity": "XH"},
+
+        # ========== 情况2: 名称+标准号都匹配,废止,有现行替代 ==========
+        # TC-SUB-01: 单条替代
+        {"id": 5, "standard_name": "起重机 钢丝绳 保养、维护、检验和报废", "standard_number": "GB/T 5972-2016", "validity": "FZ"},
+        {"id": 6, "standard_name": "起重机 钢丝绳 保养、维护、检验和报废", "standard_number": "GB/T 5972-2023", "validity": "XH"},
+
+        # ========== 情况3: 名称+标准号都匹配,废止,无替代 ==========
+        # TC-ABOL-01
+        {"id": 7, "standard_name": "缆索起重机", "standard_number": "GB/T 28756-2012", "validity": "FZ"},
+        # TC-ABOL-02
+        {"id": 8, "standard_name": "电力高处作业防坠器", "standard_number": "DL/T 1147-2009", "validity": "FZ"},
+        # TC-ABOL-03: 多版本废止
+        {"id": 9, "standard_name": "旧版施工规范", "standard_number": "TB 10001-2000", "validity": "FZ"},
+        {"id": 10, "standard_name": "旧版施工规范", "standard_number": "TB 10001-2005", "validity": "FZ"},
+
+        # ========== 情况4: 名称或标准号只有一个匹配(现行状态)==========
+        # TC-MIS-01: 标准号匹配,名称不匹配(年份错误)
+        {"id": 11, "standard_name": "公路水运危险性较大工程专项施工方案编制审查规程", "standard_number": "JT/T 1495-2024", "validity": "XH"},
+
+        # TC-MIS-02: 名称匹配,标准号不匹配
+        {"id": 12, "standard_name": "建筑地基基础设计规范", "standard_number": "GB 50007-2011", "validity": "XH"},
+
+        # TC-MIS-03: 标准号前缀匹配,名称模糊匹配
+        {"id": 13, "standard_name": "钢结构设计标准", "standard_number": "GB 50017-2017", "validity": "XH"},
+
+        # ========== 情况5: 都不匹配 ==========
+        # 数据库中没有以下标准,用于测试 NOT_FOUND
+        # TC-NF-01: 完全不存在的标准
+        # TC-NF-02: 标准号格式正确但不存在
+        # TC-NF-03: 名称部分匹配但不够相似
+
+        # ========== 边界情况 ==========
+        # TC-EDGE-01: 同一标准名称多个版本(选择最新现行)
+        {"id": 14, "standard_name": "多版本标准测试", "standard_number": "GB/T 99999-2015", "validity": "FZ"},
+        {"id": 15, "standard_name": "多版本标准测试", "standard_number": "GB/T 99999-2020", "validity": "XH"},
+        {"id": 16, "standard_name": "多版本标准测试", "standard_number": "GB/T 99999-2023", "validity": "XH"},
+
+        # TC-EDGE-02: 空标准号情况
+        {"id": 17, "standard_name": "无标准号规范", "standard_number": "DB 51/T 9999-2020", "validity": "XH"},
+
+        # TC-EDGE-03: 特殊字符名称
+        {"id": 18, "standard_name": "《特殊(字符)》规范", "standard_number": "Q/CR 9001-2020", "validity": "XH"},
+    ]
+
+
+@pytest.fixture
+def repository():
+    """创建并加载测试数据的仓库"""
+    repo = StandardRepository()
+    repo.load_data(build_mock_data())
+    return repo
+
+
+@pytest.fixture
+def matcher(repository):
+    """创建匹配器实例"""
+    return StandardMatcher(repository)
+
+
+# ========================================
+# 情况1: 正常(OK)
+# ========================================
+
+class TestCaseOK:
+    """
+    测试情况1: 名称+标准号都匹配,现行/试行
+    期望结果: status_code = OK, process_result = 正常
+    """
+
+    def test_tc_ok_01_basic_match(self, matcher):
+        """TC-OK-01: 基本匹配 - 名称和标准号完全匹配"""
+        result = matcher.match(1, "铁路桥涵设计规范", "TB 10002-2017")
+        assert result.status_code == MatchResultCode.OK.value
+        assert result.process_result == "正常"
+        assert result.final_result == "无问题"
+
+    def test_tc_ok_02_with_bookname_input(self, matcher):
+        """TC-OK-02: 输入带书名号,库中不带书名号"""
+        result = matcher.match(1, "《铁路桥涵设计规范》", "TB 10002-2017")
+        assert result.status_code == MatchResultCode.OK.value
+
+    def test_tc_ok_03_trial_status(self, matcher):
+        """TC-OK-03: 试行状态也返回正常"""
+        result = matcher.match(1, "混凝土结构耐久性设计标准", "GB/T 50476-2019")
+        assert result.status_code == MatchResultCode.OK.value
+        assert result.process_result == "正常"
+
+    def test_tc_ok_04_with_spaces(self, matcher):
+        """TC-OK-04: 名称带空格匹配"""
+        result = matcher.match(1, "铁路混凝土工程施工质量验收标准", "TB 10424-2018")
+        assert result.status_code == MatchResultCode.OK.value
+
+    def test_tc_ok_05_fuzzy_name_match(self, matcher):
+        """TC-OK-05: 名称模糊匹配(忽略书名号和空格)"""
+        # 库中带书名号,输入不带
+        result = matcher.match(1, "铁路工程抗震设计规范", "GB 50111-2006")
+        assert result.status_code == MatchResultCode.OK.value
+
+
+# ========================================
+# 情况2: 被替代(SUBSTITUTED)
+# ========================================
+
+class TestCaseSubstituted:
+    """
+    测试情况2: 名称+标准号都匹配,废止,有现行替代
+    期望结果: status_code = SUBSTITUTED, 返回替代标准信息
+    """
+
+    def test_tc_sub_01_single_replacement(self, matcher):
+        """TC-SUB-01: 单条替代标准"""
+        result = matcher.match(1, "起重机 钢丝绳 保养、维护、检验和报废", "GB/T 5972-2016")
+        assert result.status_code == MatchResultCode.SUBSTITUTED.value
+        assert result.process_result == "被替代"
+        assert result.substitute_number == "GB/T 5972-2023"
+        assert "已废止" in result.final_result
+        assert "替代" in result.final_result
+
+    def test_tc_sub_02_db_has_bookname(self, matcher):
+        """TC-SUB-02: 输入带书名号,测试名称匹配"""
+        result = matcher.match(1, "《起重机 钢丝绳 保养、维护、检验和报废》", "GB/T 5972-2016")
+        assert result.status_code == MatchResultCode.SUBSTITUTED.value
+
+
+# ========================================
+# 情况3: 废止无替代(ABOLISHED)
+# ========================================
+
+class TestCaseAbolished:
+    """
+    测试情况3: 名称+标准号都匹配,废止,无现行替代
+    期望结果: status_code = ABOLISHED, process_result = 废止无现行
+    """
+
+    def test_tc_abol_01_no_replacement(self, matcher):
+        """TC-ABOL-01: 缆索起重机,废止无替代"""
+        result = matcher.match(1, "缆索起重机", "GB/T 28756-2012")
+        assert result.status_code == MatchResultCode.ABOLISHED.value
+        assert result.process_result == "废止无现行"
+        assert "已废止,无现行状态" in result.final_result
+
+    def test_tc_abol_02_safety_device(self, matcher):
+        """TC-ABOL-02: 电力高处作业防坠器,废止无替代"""
+        result = matcher.match(1, "电力高处作业防坠器", "DL/T 1147-2009")
+        assert result.status_code == MatchResultCode.ABOLISHED.value
+        assert result.process_result == "废止无现行"
+
+    def test_tc_abol_03_multi_abolished(self, matcher):
+        """TC-ABOL-03: 多版本废止,无现行"""
+        result = matcher.match(1, "旧版施工规范", "TB 10001-2000")
+        assert result.status_code == MatchResultCode.ABOLISHED.value
+        assert "无现行状态" in result.final_result
+
+
+# ========================================
+# 情况4: 不匹配(MISMATCH)
+# ========================================
+
+class TestCaseMismatch:
+    """
+    测试情况4: 名称或标准号只有一个匹配,现行状态
+    期望结果: status_code = MISMATCH, 返回实际标准信息
+    """
+
+    def test_tc_mis_01_number_mismatch_year(self, matcher):
+        """TC-MIS-01: 标准号年份不匹配(2023 vs 2024)"""
+        result = matcher.match(1, "公路水运危险性较大工程专项施工方案编制审查规程", "JT/T 1495-2023")
+        assert result.status_code == MatchResultCode.MISMATCH.value
+        assert result.process_result == "不匹配"
+        assert result.substitute_number == "JT/T 1495-2024"
+        assert "与实际" in result.final_result
+
+    def test_tc_mis_02_number_mismatch_with_book(self, matcher):
+        """TC-MIS-02: 带书名号输入,标准号年份不匹配"""
+        result = matcher.match(1, "《公路水运危险性较大工程专项施工方案编制审查规程》", "JT/T 1495-2023")
+        assert result.status_code == MatchResultCode.MISMATCH.value
+        assert result.substitute_number == "JT/T 1495-2024"
+
+    def test_tc_mis_03_name_match_number_wrong(self, matcher):
+        """TC-MIS-03: 名称匹配,标准号完全错误"""
+        # 输入错误的年份
+        result = matcher.match(1, "铁路桥涵设计规范", "TB 10002-2020")
+        assert result.status_code == MatchResultCode.MISMATCH.value
+        assert result.substitute_number == "TB 10002-2017"
+
+    def test_tc_mis_05_number_match_name_mismatch_current(self, matcher):
+        """TC-MIS-05: 标准号匹配,名称完全不匹配,但标准为现行状态
+
+        用户场景:输入 "铁路桥涵设计" + "TB 10002-2017"
+        数据库有:"TB 10002-2017" + "铁路桥涵设计规范"(现行)
+        期望:返回 MISMATCH(不匹配),而不是 NOT_FOUND(标准库不存在)
+        """
+        result = matcher.match(1, "铁路桥涵设计", "TB 10002-2017")
+        # 标准号存在且现行,应该返回 MISMATCH 而非 NOT_FOUND
+        assert result.status_code == MatchResultCode.MISMATCH.value
+        assert result.process_result == "不匹配"
+        assert result.substitute_number == "TB 10002-2017"
+        assert "铁路桥涵设计规范" in result.substitute_name
+
+    def test_tc_mis_06_number_match_name_mismatch_abolished(self, matcher):
+        """TC-MIS-06: 标准号匹配,名称完全不匹配,但标准已废止"""
+        # 使用已废止的标准号但输入不同的名称
+        result = matcher.match(1, "某个错误名称", "GB/T 28756-2012")
+        # 标准号存在但已废止,应该返回 ABOLISHED
+        assert result.status_code == MatchResultCode.ABOLISHED.value
+        assert result.process_result == "废止无现行"
+
+
+# ========================================
+# 情况5: 不存在(NOT_FOUND)
+# ========================================
+
+class TestCaseNotFound:
+    """
+    测试情况5: 名称和标准号都不匹配
+    期望结果: status_code = NOT_FOUND
+    """
+
+    def test_tc_nf_01_completely_not_exist(self, matcher):
+        """TC-NF-01: 完全不存在的标准"""
+        result = matcher.match(1, "完全不存在的标准名称", "GB/T 99999-9999")
+        assert result.status_code == MatchResultCode.NOT_FOUND.value
+        assert result.process_result == "标准库不存在"
+        assert "标准库不存在,请确认" in result.final_result
+
+    def test_tc_nf_02_number_format_valid_but_not_exist(self, matcher):
+        """TC-NF-02: 标准号格式正确但不存在"""
+        result = matcher.match(1, "某个规范", "TB 88888-2099")
+        assert result.status_code == MatchResultCode.NOT_FOUND.value
+
+    def test_tc_nf_03_name_partial_no_match(self, matcher):
+        """TC-NF-03: 名称部分相似但不够匹配"""
+        result = matcher.match(1, "桥涵设计", "TB 10002-2017")
+        # 模糊匹配可能能找到,但精确匹配失败
+        # 根据实现可能返回 MISMATCH 或 NOT_FOUND
+        assert result.status_code in [MatchResultCode.NOT_FOUND.value, MatchResultCode.MISMATCH.value]
+
+
+# ========================================
+# 边界情况测试
+# ========================================
+
+class TestCaseEdgeCases:
+    """
+    边界情况测试
+    """
+
+    def test_tc_edge_01_multi_version_select_latest(self, matcher):
+        """TC-EDGE-01: 多版本废止,选择最新现行替代"""
+        result = matcher.match(1, "多版本标准测试", "GB/T 99999-2015")
+        assert result.status_code == MatchResultCode.SUBSTITUTED.value
+        # 应该返回最新的现行版本 2023
+        assert result.substitute_number == "GB/T 99999-2023"
+
+    def test_tc_edge_02_empty_number_then_search_by_name(self, matcher):
+        """TC-EDGE-02: 空标准号,按名称查询应该能找到"""
+        # 空标准号时,应该按名称查询
+        # 由于名称为"无标准号规范"在库中存在,应该能匹配到
+        result = matcher.match(1, "无标准号规范", "")
+        # 名称为"无标准号规范"在库中有记录,应该能匹配到(返回 MISMATCH 因为没有标准号)
+        # 或者返回 NOT_FOUND 如果空字符串不被处理
+        assert result.status_code in [MatchResultCode.MISMATCH.value, MatchResultCode.NOT_FOUND.value]
+
+    def test_tc_edge_03_special_chars_in_name(self, matcher):
+        """TC-EDGE-03: 特殊字符名称匹配"""
+        result = matcher.match(1, "《特殊(字符)》规范", "Q/CR 9001-2020")
+        assert result.status_code == MatchResultCode.OK.value
+
+    def test_tc_edge_04_input_with_fullwidth_space(self, matcher):
+        """TC-EDGE-04: 输入带中间全角空格 - 应视为不匹配"""
+        # 中间空格属于名称的一部分,应该视为不匹配
+        result = matcher.match(1, "铁路桥涵 设计规范", "TB 10002-2017")
+        # 数据库中是"铁路桥涵设计规范"(无空格),输入有全角空格,应视为不匹配
+        assert result.status_code == MatchResultCode.MISMATCH.value
+
+    def test_tc_edge_05_empty_name(self, matcher):
+        """TC-EDGE-05: 空名称输入"""
+        result = matcher.match(1, "", "TB 10002-2017")
+        # 名称为空,但标准号能匹配,返回结果取决于实现
+        assert result.status_code in [MatchResultCode.NOT_FOUND.value, MatchResultCode.MISMATCH.value]
+
+    def test_tc_edge_06_leading_trailing_spaces(self, matcher):
+        """TC-EDGE-06: 输入带前后空格(中间空格保留)"""
+        # 去除前后空格后,中间空格保留,与数据库不匹配
+        result = matcher.match(1, "  铁路桥涵 设计规范  ", "TB 10002-2017")
+        # 数据库中是"铁路桥涵设计规范"(无中间空格),输入"铁路桥涵 设计规范"(有中间空格),应视为不匹配
+        assert result.status_code == MatchResultCode.MISMATCH.value
+
+    def test_tc_edge_07_leading_trailing_spaces_in_number(self, matcher):
+        """TC-EDGE-07: 标准号带前后空格"""
+        result = matcher.match(1, "铁路桥涵设计规范", "  TB 10002-2017  ")
+        assert result.status_code == MatchResultCode.OK.value
+
+    def test_tc_edge_09_chinese_brackets_in_number(self, matcher):
+        """TC-EDGE-09: 标准号带中文括号(用户场景)"""
+        # 用户场景:标准号被中文括号包围
+        result = matcher.match(1, "铁路桥涵设计规范", "(TB 10002-2017)")
+        assert result.status_code == MatchResultCode.OK.value
+        assert result.final_result == "无问题"
+
+    def test_tc_edge_10_bookname_and_brackets(self, matcher):
+        """TC-EDGE-10: 标准名称带书名号和标准号带括号"""
+        # 用户场景:标准名称带书名号,标准号带中文括号
+        result = matcher.match(1, "《铁路桥涵设计规范》", "(TB 10002-2017)")
+        assert result.status_code == MatchResultCode.OK.value
+        assert result.final_result == "无问题"
+
+    def test_tc_edge_11_nested_brackets(self, matcher):
+        """TC-EDGE-11: 嵌套括号"""
+        result = matcher.match(1, "《《铁路桥涵设计规范》》", "((TB 10002-2017))")
+        assert result.status_code == MatchResultCode.OK.value
+
+    def test_tc_edge_12_english_brackets(self, matcher):
+        """TC-EDGE-12: 英文括号"""
+        result = matcher.match(1, "铁路桥涵设计规范", "(TB 10002-2017)")
+        assert result.status_code == MatchResultCode.OK.value
+
+
+# ========================================
+# 批量测试
+# ========================================
+
+class TestCaseBatch:
+    """
+    批量测试
+    """
+
+    @pytest.fixture
+    def mock_service(self):
+        """创建使用Mock数据的服务"""
+        service = StandardMatchingService(db_pool=None)
+        loop = asyncio.get_event_loop()
+        loop.run_until_complete(service.initialize())
+        return service
+
+    def test_batch_mixed_standards(self, mock_service):
+        """批量测试混合标准"""
+        standards = [
+            # OK
+            {"standard_name": "铁路桥涵设计规范", "standard_number": "TB 10002-2017"},
+            # SUBSTITUTED
+            {"standard_name": "起重机 钢丝绳 保养、维护、检验和报废", "standard_number": "GB/T 5972-2016"},
+            # ABOLISHED
+            {"standard_name": "缆索起重机", "standard_number": "GB/T 28756-2012"},
+            # MISMATCH
+            {"standard_name": "公路水运危险性较大工程专项施工方案编制审查规程", "standard_number": "JT/T 1495-2023"},
+            # NOT_FOUND
+            {"standard_name": "不存在的标准", "standard_number": "GB/T 99999-9999"},
+        ]
+
+        results = mock_service.check_standards(standards)
+        assert len(results) == 5
+
+        assert results[0].status_code == MatchResultCode.OK.value
+        assert results[1].status_code == MatchResultCode.SUBSTITUTED.value
+        assert results[2].status_code == MatchResultCode.ABOLISHED.value
+        assert results[3].status_code == MatchResultCode.MISMATCH.value
+        assert results[4].status_code == MatchResultCode.NOT_FOUND.value
+
+
+# ========================================
+# 运行测试
+# ========================================
+
+if __name__ == "__main__":
+    pytest.main([__file__, "-v", "--tb=short"])

+ 125 - 0
utils_test/standard_new_Test/test_user_standards.py

@@ -0,0 +1,125 @@
+"""
+测试用户提供的4个标准规范
+验证模糊名称匹配修复是否有效
+"""
+import asyncio
+import sys
+import os
+
+# 添加项目根目录到 Python 路径
+current_dir = os.path.dirname(os.path.abspath(__file__))
+project_root = os.path.dirname(os.path.dirname(current_dir))
+if project_root not in sys.path:
+    sys.path.insert(0, project_root)
+
+from utils_test.standard_new_Test.standard_service import (
+    StandardMatchingService,
+    MatchResultCode
+)
+
+
+async def test_user_standards():
+    """测试用户提供的4个标准"""
+    # 初始化服务(使用Mock数据)
+    service = StandardMatchingService(db_pool=None)
+    await service.initialize()
+
+    # 用户提供的4个标准
+    test_standards = [
+        {"standard_name": "铁路桥涵设计规范", "standard_number": "TB 10002-2017"},
+        {"standard_name": "铁路工程抗震设计规范", "standard_number": "GB 50111-2006"},
+        {"standard_name": "铁路混凝土工程施工质量验收标准", "standard_number": "TB 10424-2018"},
+        {"standard_name": "公路水运危险性较大工程专项施工方案编制审查规程", "standard_number": "JT/T 1495-2023"},
+    ]
+
+    print("=" * 80)
+    print("测试用户提供的4个标准(使用Mock数据)")
+    print("=" * 80)
+
+    results = service.check_standards(test_standards)
+
+    all_passed = True
+    for i, result in enumerate(results, 1):
+        print(f"\n【{i}】{result.original_name}")
+        print(f"    标准号: {result.original_number}")
+        print(f"    状态码: {result.status_code}")
+        print(f"    处理结果: {result.process_result}")
+        print(f"    最终结果: {result.final_result}")
+
+        # 验证预期结果
+        if i <= 3:
+            # 前3个应该是 OK
+            if result.status_code != MatchResultCode.OK.value:
+                print(f"    [失败] 期望 OK,实际 {result.status_code}")
+                all_passed = False
+            else:
+                print(f"    [通过]")
+        else:
+            # 第4个应该是 MISMATCH(因为Mock数据中是2024版)
+            if result.status_code != MatchResultCode.MISMATCH.value:
+                print(f"    [失败] 期望 MISMATCH,实际 {result.status_code}")
+                all_passed = False
+            else:
+                print(f"    [通过]")
+
+    print("\n" + "=" * 80)
+    if all_passed:
+        print("[成功] 所有测试通过!")
+    else:
+        print("[失败] 部分测试失败!")
+    print("=" * 80)
+
+    return all_passed
+
+
+def test_with_bookname_variations():
+    """测试书名号变体"""
+    from utils_test.standard_new_Test.standard_service import StandardRepository, StandardMatcher
+
+    print("\n" + "=" * 80)
+    print("测试书名号变体匹配")
+    print("=" * 80)
+
+    # 创建仓库并加载测试数据
+    repo = StandardRepository()
+    mock_data = [
+        {"id": 1, "standard_name": "铁路桥涵设计规范", "standard_number": "TB 10002-2017", "validity": "XH"},
+    ]
+    repo.load_data(mock_data)
+    matcher = StandardMatcher(repo)
+
+    # 测试各种名称变体
+    test_cases = [
+        ("铁路桥涵设计规范", "TB 10002-2017", "无书名号"),
+        ("《铁路桥涵设计规范》", "TB 10002-2017", "带书名号"),
+        ("铁路桥涵 设计规范", "TB 10002-2017", "带空格"),
+        ("《铁路桥涵 设计规范》", "TB 10002-2017", "带书名号和空格"),
+    ]
+
+    all_passed = True
+    for name, number, desc in test_cases:
+        result = matcher.match(1, name, number)
+        status = "[OK]" if result.status_code == MatchResultCode.OK.value else "[FAIL]"
+        print(f"\n{status} {desc}")
+        print(f"   输入名称: {name}")
+        print(f"   结果: {result.status_code}")
+        if result.status_code != MatchResultCode.OK.value:
+            all_passed = False
+
+    return all_passed
+
+
+if __name__ == "__main__":
+    # 运行异步测试
+    result1 = asyncio.run(test_user_standards())
+
+    # 运行书名号变体测试
+    result2 = test_with_bookname_variations()
+
+    print("\n" + "=" * 80)
+    if result1 and result2:
+        print("[成功] 所有测试通过!修复成功。")
+        sys.exit(0)
+    else:
+        print("[警告] 部分测试失败!")
+        sys.exit(1)

Datei-Diff unterdrückt, da er zu groß ist
+ 0 - 0
utils_test/standard_new_Test/施工方案审查流程图.svg


Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.