Files
playwrite/docs/MATERIAL_VALIDATION_INTERFACE.md
Misaka_Company 4fcb29f488 docs: add comprehensive material validation interface documentation
Add detailed documentation for the material validation interface including:
- System architecture with Mermaid diagrams
- Data flow visualization
- Core component descriptions
- Database table structures
- Dual-priority matching algorithm
- Permission control (Admin vs User)
- Interactive features (checkbox sync, manager filter, table editing)
- Usage scenarios and technical points

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-24 15:00:38 +08:00

31 KiB
Raw Permalink Blame History

物料校验界面实现说明文档

1. 概述

功能简介

物料校验界面是一个用于校验物料状态并管理待删除物料的核心功能模块。该界面支持两种校验模式,实现了物料状态与数据库记录的智能匹配,并提供可视化的管理界面。

核心文件路径

gui/material_validation_tab.py       - 主界面实现
utils/material_status_validator.py   - 校验器核心逻辑
db/discrete_material_plan_dao.py     - 物料数据访问
db/materials_to_be_deleted_dao.py    - 待删除类型数据访问
db/materials_to_be_deleted_records_dao.py - 已标记记录数据访问
db/production_contract_data_dao.py   - 生产合同数据访问
auth/session_manager.py              - 会话管理(权限控制)

相关数据库表

  • DiscreteMaterialPlanData - 离散备料计划数据表(主数据源)
  • MaterialsTypeToBeDeleted - 待删除物料类型表(按物料名称匹配)
  • MaterialsToBeDeleted - 已标记删除的物料记录表(按物料代码精确匹配)
  • [productionContractData].[26年压力表合同数据] - 生产合同数据表

2. 系统架构

2.1 整体架构图

graph TD
    A[用户界面层] --> B[MaterialValidationTab]
    B --> C[CheckboxTreeview]
    B --> D[控制面板]
    B --> E[负责人筛选器]
    B --> F[日志面板]

    D --> G[校验模式选择]
    G --> H[database_full<br/>全表校验]
    G --> I[database_filtered<br/>ProductionID过滤]

    H --> J[MaterialStatusValidator]
    I --> J

    J --> K[DiscreteMaterialPlanDAO]
    J --> L[ProductionContractDataDAO]
    J --> M[MaterialsTypeToBeDeletedDAO]
    J --> N[MaterialsToBeDeletedDAO]

    K --> O[(DiscreteMaterialPlanData)]
    L --> P[(26年压力表合同数据)]
    M --> Q[(MaterialsTypeToBeDeleted)]
    N --> R[(MaterialsToBeDeleted)]

    B --> S[SessionManager]
    S --> T{用户类型}
    T -->|Admin| U[完整权限]
    T -->|User| V[受限权限]

    style A fill:#e1f5ff
    style B fill:#fff4e1
    style J fill:#ffe1f5
    style O fill:#e1ffe1
    style P fill:#e1ffe1
    style Q fill:#e1ffe1
    style R fill:#e1ffe1

2.2 组件关系图

classDiagram
    class MaterialValidationTab {
        +start_validation()
        +confirm_deletion()
        +export_results()
        -_validation_worker_enhanced()
        -_apply_manager_filter()
        -_sync_checkbox_by_material_code()
    }

    class CheckboxTreeview {
        +set_checked(item, checked)
        +get_checked_items()
        +check_all(checked)
        -_on_click(event)
    }

    class MaterialStatusValidator {
        +validate_from_database_enhanced()
        +match_materials_detailed()
        -_get_source_numbers_from_production_ids()
        -_get_material_names_from_db()
    }

    class DiscreteMaterialPlanDAO {
        +query_all()
        +query_by_source_numbers()
        +get_unique_material_names()
    }

    class ProductionContractDataDAO {
        +get_source_numbers_by_总排号()
        +query_by_总排号()
    }

    class MaterialsTypeToBeDeletedDAO {
        +get_all_materials()
        +get_managers()
    }

    class MaterialsToBeDeletedDAO {
        +get_all_records()
        +upsert_batch()
        +delete_by_material_codes()
    }

    class SessionManager {
        +is_admin()
        +get_username()
        +login()
    }

    class MaterialValidationResult {
        +material_name
        +material_code
        +manager_name
        +is_marked_for_deletion
        +matched_type_keyword
    }

    MaterialValidationTab --> CheckboxTreeview : contains
    MaterialValidationTab --> MaterialStatusValidator : uses
    MaterialValidationTab --> SessionManager : uses
    MaterialStatusValidator --> DiscreteMaterialPlanDAO : queries
    MaterialStatusValidator --> ProductionContractDataDAO : queries
    MaterialStatusValidator --> MaterialsTypeToBeDeletedDAO : queries
    MaterialStatusValidator --> MaterialsToBeDeletedDAO : queries
    MaterialStatusValidator --> MaterialValidationResult : creates

3. 数据流程

3.1 校验流程图

flowchart TD
    Start([开始校验]) --> CheckMode{选择校验模式}

    CheckMode -->|database_full| FullMode[全表校验]
    CheckMode -->|database_filtered| FilterMode[过滤校验]

    FullMode --> QueryAll[查询 DiscreteMaterialPlanData<br/>获取所有记录]
    FilterMode --> ReadProductionID[读取 ProductionID.txt]
    ReadProductionID --> QueryContract[查询生产合同数据<br/>获取 SourceNumber]
    QueryContract --> QueryBySource[按 SourceNumber<br/>查询物料记录]

    QueryAll --> FetchType[获取待删除物料类型]
    QueryBySource --> FetchType

    FetchType --> GetTypeRecords[MaterialsTypeToBeDeleted<br/>获取所有记录]
    GetTypeRecords --> GetMarkedRecords[MaterialsToBeDeleted<br/>获取已标记记录]

    GetMarkedRecords --> BuildDict[构建 MaterialCode->ManagerName<br/>映射字典]
    BuildDict --> Match[执行双优先级匹配]

    Match --> Priority1{优先级1:<br/>精确匹配?}
    Priority1 -->|是| SetMarked[设置 is_marked=true<br/>使用 MaterialsToBeDeleted.ManagerName]
    Priority1 -->|否| Priority2{优先级2:<br/>模糊匹配?}

    Priority2 -->|是| SetKeyword[设置 matched_keyword<br/>使用 MaterialsTypeToBeDeleted.ManagerName]
    Priority2 -->|否| SetUnmatched[设置 manager_name=null<br/>is_marked=false]

    SetMarked --> CreateResult[创建 MaterialValidationResult]
    SetKeyword --> CreateResult
    SetUnmatched --> CreateResult

    CreateResult --> Cache[缓存结果记录]
    Cache --> Display[显示在界面表格]
    Display --> InitFilter[初始化负责人筛选器]
    InitFilter --> End([完成])

    style Start fill:#e1f5ff
    style End fill:#e1f5ff
    style Match fill:#ffe1f5
    style CreateResult fill:#fff4e1

3.2 数据查询链路图

flowchart LR
    A[ProductionID.txt<br/>总排号列表] --> B[ProductionContractDataDAO]
    B --> C[[26年压力表合同数据]]
    C --> D[SourceNumber<br/>生产订单号]

    D --> E[DiscreteMaterialPlanDAO]
    E --> F[[DiscreteMaterialPlanData]]
    F --> G[物料记录列表<br/>MaterialCode, MaterialName<br/>Specification, Model]

    G --> H[MaterialStatusValidator]
    H --> I[MaterialsTypeToBeDeletedDAO]
    H --> J[MaterialsToBeDeletedDAO]

    I --> K[[MaterialsTypeToBeDeleted]]
    J --> L[[MaterialsToBeDeleted]]

    K --> M[物料类型匹配<br/>MaterialName 包含匹配]
    L --> N[精确匹配<br/>MaterialCode 精确匹配]

    M --> O[双优先级匹配算法]
    N --> O

    O --> P[MaterialValidationResult<br/>校验结果]

    style A fill:#ffe1f5
    style F fill:#ffe1f5
    style K fill:#ffe1f5
    style L fill:#ffe1f5
    style P fill:#e1f5ff

3.3 交互时序图

sequenceDiagram
    actor User as 用户
    participant UI as MaterialValidationTab
    participant Validator as MaterialStatusValidator
    participant DAO1 as DiscreteMaterialPlanDAO
    participant DAO2 as ProductionContractDataDAO
    participant DAO3 as MaterialsTypeToBeDeletedDAO
    participant DAO4 as MaterialsToBeDeletedDAO

    User->>UI: 点击"开始校验"
    UI->>UI: 验证输入文件
    UI->>Validator: 创建校验器实例
    UI->>UI: 启动后台线程

    alt 全表校验模式
        Validator->>DAO1: query_all()
        DAO1-->>Validator: 返回所有物料记录
    else ProductionID过滤模式
        Validator->>DAO2: get_source_numbers_by_总排号()
        DAO2-->>Validator: 返回 SourceNumber 列表
        Validator->>DAO1: query_by_source_numbers()
        DAO1-->>Validator: 返回过滤后的物料记录
    end

    Validator->>DAO3: get_all_materials()
    DAO3-->>Validator: 返回物料类型记录

    Validator->>DAO4: get_all_records()
    DAO4-->>Validator: 返回已标记记录

    Validator->>Validator: match_materials_detailed()
    Note over Validator: 执行双优先级匹配

    Validator-->>UI: 返回校验结果
    UI->>UI: 缓存结果记录
    UI->>UI: 更新表格显示
    UI->>UI: 初始化负责人筛选器

    UI-->>User: 显示校验完成

4. 核心组件说明

4.1 MaterialValidationTab 类

职责: 物料校验标签页的主界面类

核心方法:

方法名 功能说明
start_validation() 启动校验流程,根据选择的模式调用相应的校验方法
_validation_worker_enhanced() 后台工作线程,执行增强的数据库校验
confirm_deletion() 确认删除操作主流程,处理勾选状态
_execute_sync_in_background() 后台执行数据库同步操作upsert/delete
_apply_manager_filter() 应用负责人筛选,更新表格显示
_sync_checkbox_by_material_code() 同步相同材料代码的所有记录的选择状态
_load_results_with_deletion_status() 加载结果并设置 checkbox 选中状态
export_results() 导出结果到 Excel 文件

权限控制:

  • Admin 用户: 可以选择数据来源(全表/过滤),可以看到所有负责人的数据,可以使用负责人筛选
  • User 用户: 只能使用 ProductionID 过滤模式,只能看到自己的数据,自动筛选到当前用户

4.2 CheckboxTreeview 类

职责: 支持复选框功能的 Treeview 组件

核心特性:

  • 使用 Unicode 字符 模拟 checkbox
  • 支持单个点击切换状态
  • 支持全选/取消全选操作
  • 支持状态变化回调

核心方法:

方法名 功能说明
set_checked(item, checked) 设置指定 item 的 checkbox 状态
get_checked_items() 获取所有选中的 item
check_all(checked) 全选或取消全选
_on_click(event) 处理点击事件,切换 checkbox 状态

4.3 MaterialStatusValidator 类

职责: 物料状态校验器,负责数据查询和匹配逻辑

核心方法:

方法名 功能说明
validate_from_database_enhanced() 增强的数据库校验(完整记录模式)
match_materials_detailed() 匹配物料并返回详细结果
_read_production_ids() 读取 ProductionID.txt 文件
_get_source_numbers_from_production_ids() 通过总排号查询获取生产订单号
_get_material_names_from_db() 从数据库获取材料名称

数据结构:

@dataclass
class MaterialValidationResult:
    material_name: str              # 材料名称
    material_code: str              # 材料代码
    specification: Optional[str]    # 规格
    model: Optional[str]            # 型号
    manager_name: Optional[str]     # 负责人
    is_marked_for_deletion: bool    # 是否已标记删除
    matched_type_keyword: Optional[str]  # 匹配的关键词

5. 数据库表结构

5.1 DiscreteMaterialPlanData离散备料计划数据表

用途: 存储离散备料计划的主数据,是校验的主要数据源

关键字段:

字段名 类型 说明
PlanNumber varchar 备料计划单号
SourceNumber varchar 来源单号(生产订单号)
MaterialCode varchar 材料编码(用于精确匹配)
MaterialName varchar 材料名称
Specification varchar 规格
Model varchar 型号
ManagerName varchar 负责人

查询示例:

# 查询所有记录
dao = DiscreteMaterialPlanDAO()
records = dao.query_all()

# 按生产订单号查询
records = dao.query_by_source_numbers(source_numbers)

# 获取唯一材料名称
material_names = dao.get_unique_material_names(source_numbers)

5.2 MaterialsTypeToBeDeleted待删除物料类型表

用途: 存储按物料名称匹配的待删除物料(模糊匹配)

关键字段:

字段名 类型 说明
MaterialName varchar 物料名称(用于包含匹配)
ManagerName varchar 负责人

匹配规则: 如果 DiscreteMaterialPlanData.MaterialName 包含 MaterialsTypeToBeDeleted.MaterialName,则匹配成功

查询示例:

dao = MaterialsTypeToBeDeletedDAO()

# 获取所有物料类型
materials = dao.get_all_materials()

# 获取所有负责人
managers = dao.get_managers()

# 按负责人查询
materials = dao.get_materials_by_manager('张三')

5.3 MaterialsToBeDeleted已标记删除的物料记录表

用途: 存储已标记删除的具体物料记录(精确匹配)

关键字段:

字段名 类型 说明
ID int 主键
MaterialCode varchar 物料代码(用于精确匹配)
ManagerName varchar 负责人

匹配规则: 如果 DiscreteMaterialPlanData.MaterialCode 等于 MaterialsToBeDeleted.MaterialCode,则匹配成功

操作示例:

dao = MaterialsToBeDeletedDAO()

# 获取所有记录
records = dao.get_all_records()

# 批量插入/更新
stats = dao.upsert_batch([
    {'material_code': 'M001', 'manager_name': '张三'},
    {'material_code': 'M002', 'manager_name': '李四'}
])

# 按物料代码删除
dao.delete_by_material_code('M001')

# 批量删除
dao.delete_by_material_codes(['M001', 'M002', 'M003'])

6. 匹配算法逻辑

6.1 双优先级匹配机制

物料校验采用双优先级匹配机制,确保精确匹配优先于模糊匹配:

flowchart TD
    Start[物料记录] --> CheckP1{优先级1:<br/>MaterialsToBeDeleted<br/>精确匹配?}

    CheckP1 -->|MaterialCode 精确匹配| Marked[已标记删除]
    Marked --> SetM1[设置 ManagerName<br/>= MaterialsToBeDeleted.ManagerName]
    SetM1 --> SetFlag1[is_marked_for_deletion = true]
    SetFlag1 --> End1[返回结果]

    CheckP1 -->|未匹配| CheckP2{优先级2:<br/>MaterialsTypeToBeDeleted<br/>模糊匹配?}

    CheckP2 -->|MaterialName 包含匹配| Keyword[匹配到关键词]
    Keyword --> SetM2[设置 ManagerName<br/>= MaterialsTypeToBeDeleted.ManagerName]
    SetM2 --> SetKeyword[matched_type_keyword<br/>= 匹配的 MaterialName]
    SetKeyword --> SetFlag2[is_marked_for_deletion = false]
    SetFlag2 --> End2[返回结果]

    CheckP2 -->|未匹配| Unmatched[未匹配]
    Unmatched --> SetNull[manager_name = null]
    SetNull --> SetFlag3[is_marked_for_deletion = false<br/>matched_type_keyword = null]
    SetFlag3 --> End3[返回结果]

    style Start fill:#e1f5ff
    style Marked fill:#ffe1f5
    style Keyword fill:#fff4e1
    style Unmatched fill:#f5f5f5

6.2 匹配代码实现

def match_materials_detailed(
    self,
    material_records: List[Dict[str, Any]],
    type_keywords: List[Dict[str, Any]],
    marked_codes_dict: Dict[str, str]
) -> List[MaterialValidationResult]:
    """
    双优先级匹配算法

    Args:
        material_records: DiscreteMaterialPlanData 的完整记录
        type_keywords: MaterialsTypeToBeDeleted 记录(模糊匹配)
        marked_codes_dict: MaterialsToBeDeleted 的 MaterialCode->ManagerName 映射(精确匹配)

    Returns:
        List[MaterialValidationResult]: 匹配结果列表
    """
    results = []

    for record in material_records:
        material_name = record.get('MaterialName', '') or ''
        material_code = record.get('MaterialCode', '') or ''
        specification = record.get('Specification', '') or None
        model = record.get('Model', '') or None

        # ===== 优先级 1: 精确匹配 =====
        # 检查 MaterialsToBeDeleted 表MaterialCode 精确匹配)
        # 这是最高优先级 - 如果 MaterialCode 存在,使用其 ManagerName
        manager_name = marked_codes_dict.get(material_code) if material_code else None
        is_marked = manager_name is not None
        matched_keyword = None

        # ===== 优先级 2: 模糊匹配 =====
        # 如果不在 MaterialsToBeDeleted 中,匹配 MaterialsTypeToBeDeleted
        # MaterialName 包含匹配)
        if not manager_name:
            for type_record in type_keywords:
                type_material_name = type_record.get('MaterialName', '')
                if type_material_name and type_material_name in material_name:
                    matched_keyword = type_material_name
                    manager_name = type_record.get('ManagerName')
                    break

        result = MaterialValidationResult(
            material_name=material_name,
            material_code=material_code,
            specification=specification,
            model=model,
            manager_name=manager_name,
            is_marked_for_deletion=is_marked,
            matched_type_keyword=matched_keyword
        )
        results.append(result)

    return results

6.3 匹配示例

MaterialCode MaterialName MaterialsToBeDeleted MaterialsTypeToBeDeleted 匹配结果 is_marked ManagerName
M001 螺栓 M8×20 M001 → 张三 - 优先级1精确匹配 true 张三
M002 垫圈 Φ8 - 垫圈 → 李四 优先级2模糊匹配 false 李四
M003 螺母 M6 - - 未匹配 false null
M004 不锈钢螺栓 M10×30 M004 → 王五 螺栓 → 赵六 优先级1精确匹配忽略螺栓 true 王五

7. 权限控制

7.1 用户类型

系统通过 SessionManager 实现基于角色的访问控制RBAC

classDiagram
    class SessionManager {
        <<Singleton>>
        -_current_user: Dict
        +login(username, password)
        +is_admin() bool
        +get_username() str
        +get_user_type() str
    }

    class User {
        <<Abstract>>
        +username: str
        +user_type: str
    }

    class Admin {
        +user_type: 'Admin'
        +can_select_data_source: true
        +can_view_all_managers: true
        +can_use_manager_filter: true
    }

    class NormalUser {
        +user_type: 'User'
        +can_select_data_source: false
        +can_view_all_managers: false
        +can_use_manager_filter: false
    }

    SessionManager --> User : manages
    User <|-- Admin
    User <|-- NormalUser

7.2 Admin vs User 权限差异

功能 Admin User
数据来源选择 可选择全表/过滤 仅限过滤模式
ProductionID 数据源 可选择文件或共享 仅限共享 ID
查看数据范围 所有负责人的数据 仅自己的数据
负责人筛选 可使用复选框筛选 自动筛选到当前用户
编辑负责人 可编辑任何人 可编辑任何人
删除确认 可操作所有人 可操作所有人

7.3 权限检查实现

# 检查是否为管理员
if self.session_manager.is_admin():
    # 显示管理员专属控件
    self.source_mode = tk.StringVar(value="database_full")
    # 显示负责人筛选区域
    manager_filter_frame = ttk.LabelFrame(...)
else:
    # 普通用户默认设置
    self.source_mode = tk.StringVar(value="database_filtered")
    # 隐藏管理员专属控件
    manager_filter_frame = ttk.Frame(...)

7.4 数据过滤逻辑

def _get_selected_managers(self) -> List[str]:
    """获取选中的负责人列表"""
    # PERMISSION CHECK: 非管理员用户直接返回当前用户名
    if not self.session_manager.is_admin():
        return [self.session_manager.get_username()]

    # 管理员:从复选框获取选中的负责人
    return [
        manager for manager, var in self.manager_checkboxes.items()
        if var.get()
    ]

8. 交互功能

8.1 复选框同步机制

功能: 当用户点击某个记录的 checkbox 时,自动将所有具有相同材料代码的记录的 checkbox 状态同步更新。

实现逻辑:

flowchart TD
    UserClick[用户点击 checkbox] --> GetItem[获取点击的 item]
    GetItem --> GetCode[获取该行的 MaterialCode]
    GetCode --> Iterate[遍历表格所有行]

    Iterate --> CheckCode{MaterialCode<br/>相同?}
    CheckCode -->|是| CheckState{状态<br/>不同?}
    CheckState -->|是| Update[更新 checkbox 状态]
    CheckState -->|否| Next[继续下一行]
    CheckCode -->|否| Next

    Update --> Next
    Next --> MoreRows{还有行?}
    MoreRows -->|是| Iterate
    MoreRows -->|否| Log[记录同步数量]
    Log --> End[完成]

    style UserClick fill:#e1f5ff
    style Update fill:#ffe1f5
    style End fill:#e1ffe1

代码实现:

def _sync_checkbox_by_material_code(self, changed_item: str, new_state: bool):
    """同步相同材料代码的所有记录的选择状态"""
    # 获取被点击行的材料代码
    values = self.tree.item(changed_item, "values")
    if not values or len(values) <= 2:
        return
    material_code = values[2]  # 材料代码在第3列索引2

    # 同步所有具有相同材料代码的记录
    synced_count = 0
    for item in self.tree.get_children():
        item_values = self.tree.item(item, "values")
        if item_values and len(item_values) > 2:
            if item_values[2] == material_code and item != changed_item:
                # 只更新状态不同的行,避免重复更新
                current_state = self.tree.checkboxes.get(item, False)
                if current_state != new_state:
                    self.tree.set_checked(item, new_state)
                    synced_count += 1

    if synced_count > 0:
        action = "选中" if new_state else "取消选中"
        self.log_text.info(f"已同步 {synced_count} 条相同材料代码的记录{action}")

8.2 负责人筛选

功能: 管理员可以通过复选框筛选显示特定负责人的物料记录。

界面布局:

┌─────────────────────────────────────────────────────────────┐
│ 筛选(按负责人)                                             │
├─────────────────────────────────────────────────────────────┤
│ ☑ 全选  ☑ 张三  ☑ 李四  ☑ 王五  ☑ 赵六  ☑ 钱七            │
│ ☑ 孙八  ☑ 周九  ☑ 吴十  ☑ 郑十一  ☑ 陈十二  ☑ 沈十三      │
│                                                               │
│ ┌─────────┐  ┌──────────┐                                    │
│ │  全选   │  │ 取消全选  │                                    │
│ └─────────┘  └──────────┘                                    │
└─────────────────────────────────────────────────────────────┘

筛选规则:

  • 显示选中负责人的记录
  • 同时显示负责人为空的记录(待编辑)
  • 未选中任何负责人时,表格清空

代码实现:

def _apply_manager_filter(self):
    """应用负责人筛选"""
    if not self.material_records_cache:
        return

    # 获取选中的负责人列表
    selected_managers = self._get_selected_managers()

    if not selected_managers:
        # 没有选中任何负责人,清空表格
        self._refresh_filtered_results([])
        self._update_log("未选择任何负责人,表格已清空", "WARNING")
        return

    # 筛选记录:包含选中负责人的记录 + 负责人为空的记录
    filtered_records = [
        record for record in self.material_records_cache
        if (record.manager_name in selected_managers or
            not record.manager_name or record.manager_name.strip() == "")
    ]

    self._refresh_filtered_results(filtered_records)
    self._update_log(f"筛选结果:共 {len(filtered_records)} 条记录", "INFO")

8.3 表格编辑

功能: 支持双击"负责人"单元格进行编辑。

编辑流程:

sequenceDiagram
    actor User
    participant Tree as CheckboxTreeview
    participant Dialog as SimpleDialog
    participant Log as LogText

    User->>Tree: 双击"负责人"单元格
    Tree->>Tree: 识别点击位置和列
    Tree->>Tree: 获取当前值
    Tree->>Dialog: 弹出编辑对话框
    Dialog-->>User: 显示输入框
    User->>Dialog: 输入新值
    Dialog-->>Tree: 返回新值
    Tree->>Tree: 更新单元格显示
    Tree->>Log: 记录更新日志
    Log-->>User: 显示更新成功消息

代码实现:

def _on_cell_double_click(self, event):
    """处理单元格双击事件,编辑负责人"""
    # 获取点击位置
    region = self.tree.identify_region(event.x, event.y)

    if region == "cell":
        column = self.tree.identify_column(event.x)
        item = self.tree.identify_row(event.y)

        # 检查是否点击了"负责人"列第6列
        if column == "#6" and item:
            values = self.tree.item(item, "values")
            current_value = values[5] if len(values) > 5 else ""

            # 弹出编辑对话框
            new_value = simpledialog.askstring(
                "编辑负责人",
                f"请输入负责人姓名:",
                initialvalue=current_value
            )

            if new_value is not None:  # 用户没有取消
                # 更新单元格值
                new_values = list(values)
                new_values[5] = new_value
                self.tree.item(item, values=new_values)

                self.log_text.info(f"已更新负责人: {current_value} -> {new_value}")

9. 使用场景

9.1 场景一:管理员全表校验

需求: 管理员需要查看所有物料记录并进行校验

操作步骤:

  1. 选择"数据库 - 全表校验"模式
  2. 点击"开始校验"
  3. 系统查询整个 DiscreteMaterialPlanData
  4. 使用负责人筛选器筛选特定负责人
  5. 双击编辑负责人
  6. 勾选需要删除的记录
  7. 点击"确认删除"保存到数据库

9.2 场景二:普通用户按 ProductionID 校验

需求: 普通用户需要校验特定生产订单的物料

操作步骤:

  1. 在"数据提取"页面输入 ProductionID
  2. 切换到"物料校验"标签页
  3. 系统自动使用共享的 ProductionID
  4. 点击"开始校验"
  5. 仅显示当前用户负责的物料
  6. 编辑负责人或勾选删除
  7. 点击"确认删除"保存

9.3 场景三:批量删除确认

需求: 用户需要批量确认删除多个物料记录

操作流程:

flowchart TD
    Start([开始]) --> Select[选择物料记录]
    Select --> CheckAll[点击全选按钮]
    CheckAll --> AutoSync[系统自动同步相同 MaterialCode 的记录]
    AutoSync --> Confirm[点击确认删除按钮]
    Confirm --> Validate{验证负责人}

    Validate -->|有缺失| ShowWarning[显示警告并列出缺少负责人的记录]
    ShowWarning --> Edit[用户编辑缺失的负责人]
    Edit --> Confirm

    Validate -->|全部完整| ShowConfirm[显示确认对话框]
    ShowConfirm --> UserConfirm{用户确认?}

    UserConfirm -->|否| Cancel[取消操作]
    UserConfirm -->|是| Execute[执行后台同步]

    Execute --> Upsert[执行 upsert_batch 写入/更新勾选记录]
    Execute --> Delete[执行 delete_by_material_codes 删除未勾选记录]

    Upsert --> Complete[显示完成消息]
    Delete --> Complete
    Complete --> Refresh[刷新筛选器和结果]
    Refresh --> End([结束])

    Cancel --> End

    style Start fill:#e1f5ff
    style End fill:#e1f5ff
    style ShowWarning fill:#fff4e1
    style Execute fill:#ffe1f5
    style Complete fill:#e1ffe1

10. 技术要点

10.1 线程安全

校验操作在后台线程中执行,避免阻塞 UI

# 在后台线程中执行校验
validation_thread = threading.Thread(
    target=self._validation_worker_enhanced,
    args=(mode, input_file, production_id_file, output_file, production_ids_list),
    daemon=True,
)
validation_thread.start()

日志更新通过 after() 方法确保线程安全:

def _update_log(self, message: str, level: str = "INFO"):
    """线程安全的日志更新"""
    def update():
        if level == "INFO":
            self.log_text.info(message)
        elif level == "ERROR":
            self.log_text.error(message)
        # ...

    self.after(0, update)  # 在主线程中执行

10.2 数据库兼容性

系统通过 _convert_sql()_get_placeholder() 方法实现 SQL Server 和 MySQL 的兼容:

# SQL Server
SELECT [MaterialName], [ManagerName]
FROM [dbo].[MaterialsTypeToBeDeleted]
WHERE [MaterialName] = ?

# MySQL
SELECT MaterialName, ManagerName
FROM `MaterialsTypeToBeDeleted`
WHERE MaterialName = ?

10.3 批量操作优化

为避免 SQL Server 参数限制2100 个),批量操作采用分批处理:

def _batch_insert(self, db, df: pd.DataFrame, batch_size: int = 72) -> int:
    """
    Batch insert records (max 72 per batch due to SQL Server 2100 param limit).
    With 29 fields, the maximum batch size is floor(2100 / 29) = 72 records.
    """
    total_inserted = 0
    records = self._convert_df_to_records(df)

    for i in range(0, len(records), batch_size):
        batch = records[i:i + batch_size]
        for record in batch:
            db.execute_update(sql, record)
            total_inserted += 1

    return total_inserted

11. 常见问题

Q1: 为什么同一个 MaterialCode 会有多条记录?

A: 因为同一个物料代码可能出现在不同的备料计划单号或生产订单号中。复选框同步机制确保了相同 MaterialCode 的记录会被一起选中/取消。

Q2: 已标记删除is_marked=true和匹配到关键词有什么区别

A:

  • 已标记删除: 来自 MaterialsToBeDeleted 表,通过 MaterialCode 精确匹配,优先级最高
  • 匹配到关键词: 来自 MaterialsTypeToBeDeleted 表,通过 MaterialName 包含匹配,优先级次之

Q3: User 用户为什么看不到"数据来源"选项?

A: 这是权限控制的设计。User 用户只能使用 ProductionID 过滤模式,确保他们只能访问相关的数据,而不是整个数据库。

Q4: 编辑负责人后,为什么需要重新点击"确认删除"

A: 编辑负责人只是修改了界面显示,并未保存到数据库。只有点击"确认删除"后,修改才会被写入数据库。

Q5: 如何理解"双优先级匹配"

A:

  1. 优先级1精确匹配: 如果 MaterialCodeMaterialsToBeDeleted 表中存在,使用该表的 ManagerName,并标记为 is_marked=true
  2. 优先级2模糊匹配: 如果优先级1未匹配检查 MaterialName 是否包含 MaterialsTypeToBeDeleted 表中的任何 MaterialName,如果包含,使用该表的 ManagerName

12. 扩展建议

12.1 性能优化

  • 对于大数据量的全表查询,考虑添加分页功能
  • 实现查询结果缓存,减少重复查询

12.2 功能增强

  • 添加批量导入功能,支持从 Excel 导入负责人信息
  • 实现导出模板功能,方便离线编辑
  • 添加校验历史记录,追溯修改历史

12.3 用户体验

  • 实现拖拽排序功能
  • 添加列筛选和排序功能
  • 支持自定义列显示/隐藏

文档版本: 1.0 最后更新: 2026-02-24 维护者: Development Team