Files
claudeskill/demo_scripts/demo.md
Misaka_Company 1351371bd2 feat: Add mermaid-fixer skill for automatic Mermaid syntax error correction
- Add skill that uses check-mermaid.js to validate Mermaid diagrams
- Skill guides Claude to parse error reports and apply intelligent fixes
- Fixes common Mermaid parser bugs (parentheses, brackets, braces in labels)
- Include demo scripts with test Markdown file for validation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 13:32:55 +08:00

15 KiB
Raw Blame History

Admin 用户物料清理数据流说明文档

概述

本文档详细说明 ERPAuto 系统中Admin 用户执行物料清理操作时,被清理物料的完整获取流程、数据来源和处理链路。

核心结论

Admin 用户清理的物料来源:被清理的物料代码从数据库表 dbo.MaterialsToBeDeleted 中获取,根据 Admin 用户在 UI 界面选择的负责人Manager进行过滤。


数据流总览

sequenceDiagram
    participant UI as CleanerPage (UI)
    participant Hook as useCleaner Hook
    participant API as Renderer API
    participant IPC as IPC Channel
    participant Service as ValidationService
    participant DB as Database

    UI->>Hook: handleExecuteDeletion()
    Hook->>API: runCleanerExecution()
    API->>IPC: validation.getCleanerData({selectedManagers})

    IPC->>Service: getCleanerData(userInfo, selectedManagers)

    alt Admin User
        Service->>Service: loadMaterialCodesForCleaner()
        Service->>DB: SELECT MaterialCode FROM dbo.MaterialsToBeDeleted<br/>WHERE ManagerName IN (selectedManagers)
        DB-->>Service: materialCodes[]
        Service-->>IPC: {orderNumbers[], materialCodes[]}
    else Regular User
        Service->>DB: SELECT MaterialCode FROM dbo.MaterialsToBeDeleted<br/>WHERE ManagerName = username
        DB-->>Service: materialCodes[]
        Service-->>IPC: {orderNumbers[], materialCodes[]}
    end

    IPC-->>API: cleanerData
    API->>IPC: cleaner.runCleaner({orderNumbers, materialCodes})
    IPC->>Service: CleanerApplicationService.runCleaner()
    Service->>UI: 执行清理 (ERP 删除)

架构分层

graph TB
    subgraph "渲染进程 (Renderer)"
        UI[CleanerPage.tsx]
        HOOK[useCleaner.ts]
        RAPI[renderer/api.ts]
    end

    subgraph "主进程 (Main)"
        IPC_VAL[IPC: validation.getCleanerData]
        IPC_CLEAN[IPC: cleaner.runCleaner]
        VAS[ValidationApplicationService]
        CAS[CleanerApplicationService]
        ERP["CleanerService (ERP)"]
    end

    subgraph "数据库 (Database)"
        MTBD[(dbo.MaterialsToBeDeleted)]
        DMPD[(dbo.DiscreteMaterialPlanData)]
        MTTD[(dbo.MaterialsTypeToBeDeleted)]
    end

    UI --> HOOK
    HOOK --> RAPI
    RAPI --> IPC_VAL
    RAPI --> IPC_CLEAN
    IPC_VAL --> VAS
    IPC_CLEAN --> CAS
    VAS --> MTBD
    VAS --> DMPD
    VAS --> MTTD
    CAS --> ERP

    style MTBD fill:#f9f,stroke:#333
    style MTTD fill:#f9f,stroke:#333

关键数据表

1. dbo.MaterialsToBeDeleted (核心来源表)

作用:存储所有被标记为待删除的物料代码及其负责人。

表结构

字段 类型 说明
ID int 主键
MaterialCode varchar 物料代码 (被清理的目标)
ManagerName varchar 负责人姓名

Admin 获取物料的 SQL 查询

SELECT MaterialCode
FROM dbo.MaterialsToBeDeleted
WHERE ManagerName IN (@manager0, @manager1, ...)
  AND MaterialCode IS NOT NULL

2. dbo.MaterialsTypeToBeDeleted (物料类型配置表)

作用:定义物料名称关键词与负责人的映射关系,用于自动分配负责人。

表结构

字段 类型 说明
MaterialName varchar 物料名称关键词
ManagerName varchar 对应的负责人

示例

MaterialName ManagerName
"电阻" "张三"
"电容" "李四"

3. dbo.DiscreteMaterialPlanData (物料计划数据表)

作用:存储从 ERP 提取的完整物料计划数据,用于校验和展示物料详情。

关键字段

  • MaterialCode - 物料代码
  • MaterialName - 物料名称
  • Specification - 规格
  • Model - 型号
  • SourceNo - 订单号

Admin 用户完整数据流

阶段 1: 校验 (Validation)

graph LR
    subgraph "步骤 1: 用户触发的数据来源"
        A1[Admin 点击'数据校验']
    end

    subgraph "步骤 2: 数据获取模式"
        A2{校验模式?}
        A21[Full Mode<br/>全量数据]
        A22[Filtered Mode<br/>筛选数据]
    end

    subgraph "步骤 3: 数据库查询"
        A3[DiscreteMaterialPlanDAO]
        A31["queryAllDistinctByMaterialCode()"]
        A32["queryBySourceNumbersDistinct(orderNumbers)"]
    end

    subgraph "步骤 4: 数据增强"
        A4[加载负责人信息]
        A41[加载 MaterialsTypeToBeDeleted<br/>关键词匹配]
        A42[加载 MaterialsToBeDeleted<br/>已标记记录]
    end

    subgraph "结果"
        A5["ValidationResult[]<br/>包含 isMarkedForDeletion 标志"]
    end

    A1 --> A2
    A2 -->|Full| A21
    A2 -->|Filtered| A22
    A21 --> A31
    A22 --> A32
    A31 --> A4
    A32 --> A4
    A4 --> A41
    A4 --> A42
    A4 --> A5

    style A5 fill:#9f9,stroke:#333

Admin 特殊逻辑

  • Admin 可以看到所有负责人的物料
  • UI 会显示 Manager 列下拉筛选器
  • Admin 默认选中所有 Manager

阶段 2: 保存删除计划 (可选)

用户在 UI 上勾选物料 → 点击"确认删除" → 数据写入 MaterialsToBeDeleted 表:

sequenceDiagram
    participant UI as 用户界面
    participant DAO as MaterialsToBeDeletedDAO
    participant DB as Database

    UI->>DAO: upsertBatch(materials[])

    loop 对每个物料
        DAO->>DB: MERGE INTO dbo.MaterialsToBeDeleted<br/>ON MaterialCode<br/>WHEN MATCHED UPDATE<br/>WHEN NOT MATCHED INSERT
    end

    DB-->>DAO: 成功/失败统计
    DAO-->>UI: { total, success, failed }

SQL 逻辑MERGE UPSERT

MERGE INTO dbo.MaterialsToBeDeleted AS target
USING (VALUES (@MaterialCode, @ManagerName)) AS source (MaterialCode, ManagerName)
ON target.MaterialCode = source.MaterialCode
WHEN MATCHED THEN
    UPDATE SET ManagerName = source.ManagerName
WHEN NOT MATCHED THEN
    INSERT (MaterialCode, ManagerName) VALUES (source.MaterialCode, source.ManagerName);

阶段 3: 执行清理 (关键步骤)

这是 Admin 用户执行实际删除操作的核心流程:

graph TD
    subgraph "Renderer 层"
        S1["handleExecuteDeletion()"]
        S1-->S2["runCleanerExecution {selectedManagers}"]
    end

    subgraph "获取清理数据 (主进程)"
        S2-->S3[ValidationApplicationService.getCleanerData]
        S3-->S4{用户类型?}

        S4-->|Admin + selectedManagers| S5[loadMaterialCodesForCleaner<br/>Admin With Managers]
        S5-->S6[SELECT FROM MaterialsToBeDeleted<br/>WHERE ManagerName IN selectedManagers]

        S4-->|Admin no managers| S7[Admin Without Managers]
        S7-->S8[SELECT FROM DiscreteMaterialPlanData<br/>WHERE SourceNo IN orderNumbers]

        S4-->|普通用户 | S9[SELECT FROM MaterialsToBeDeleted<br/>WHERE ManagerName = username]
    end

    subgraph "执行清理"
        S6-->S10["返回 materialCodes[]"]
        S8-->S10
        S9-->S10
        S10-->S11[CleanerApplicationService.runCleaner]
        S11-->S12[CleanerService.clean<br/>连接 ERP 执行删除]
    end

    S12-->S13[清理完成报告]

    style S5 fill:#ff9,stroke:#333
    style S6 fill:#f96,stroke:#333
    style S12 fill:#f99,stroke:#333

关键代码路径

// src/main/services/validation/validation-application-service.ts
// loadMaterialCodesForCleaner() 方法

// Admin with selected managers:
if (isAdmin && selectedManagers && selectedManagers.length > 0) {
  const materialCodes = await this.queryMaterialCodesByManagers(
    dbService,
    markedTableName, // dbo.MaterialsToBeDeleted
    selectedManagers
  )
  return materialCodes
}

// Admin without selected managers (fallback):
if (isAdmin) {
  if (orderNumbers.length === 0) {
    return [] // 无数据可处理
  }
  const materialDao = new DiscreteMaterialPlanDAO()
  const records = await materialDao.queryBySourceNumbersDistinct(orderNumbers)
  const materialCodes = [...new Set(records.map((r) => r.MaterialCode as string).filter(Boolean))]
  return materialCodes
}

阶段 4: ERP 删除执行

sequenceDiagram
    participant CAS as CleanerApplicationService
    participant Cleaner as CleanerService
    participant ERP as ERP System

    CAS->>Cleaner: clean({orderNumbers, materialCodes})

    loop 每个订单号
        Cleaner->>ERP: 登录 (一次)
        loop 每个物料代码
            Cleaner->>ERP: 查询物料计划
            alt 物料存在
                Cleaner->>ERP: 执行删除
                ERP-->>Cleaner: 删除结果
            else 物料不存在
                Cleaner-->>Cleaner: 跳过
            end
        end
    end

    Cleaner-->>CAS: CleanerResult

Admin 用户的数据来源路径

整理出 Admin 用户获取物料的完整路径:

graph TB
    Start[Admin 点击执行清理] --> Check{有 selectedManagers?}

    Check -->|Yes| Path1[路径 1: MaterialsToBeDeleted 表]
    Path1 --> Q1["SELECT MaterialCode FROM MaterialsToBeDeleted<br/>WHERE ManagerName IN (selectedManagers)"]
    Q1 --> Merge[合并所有物料代码]

    Check -->|No| Path2[路径 2: DiscreteMaterialPlanData 表]
    Path2 --> NeedProd{有共享 Production ID?}
    NeedProd -->|Yes| GetOrder[从共享 ID 解析订单号]
    GetOrder --> Q2["SELECT DISTINCT MaterialCode FROM DiscreteMaterialPlanData<br/>WHERE SourceNo IN (orderNumbers)"]
    Q2 --> Merge

    NeedProd -->|No| Empty[返回空数组<br/>无法执行清理]

    Merge --> Execute[传递给 CleanerService<br/>执行 ERP 删除]

    style Path1 fill:#9f9,stroke:#333,stroke-width:2px
    style Path2 fill:#ff9,stroke:#333,stroke-width:2px
    style Empty fill:#f99,stroke:#333
    style Execute fill:#f96,stroke:#333,stroke-width:3px

关键代码位置参考

渲染层 (Renderer)

文件 函数 说明
src/renderer/src/pages/CleanerPage.tsx handleExecuteDeletion 清理入口
src/renderer/src/hooks/useCleaner.ts handleExecuteDeletion 调用 runCleanerExecution
src/renderer/src/hooks/cleaner/api.ts runCleanerExecution 先获取数据再执行清理

主进程层 (Main)

文件 类/函数 说明
src/main/ipc/validation-handler.ts VALIDATION_GET_CLEANER_DATA IPC 入口
src/main/services/validation/validation-application-service.ts getCleanerData, loadMaterialCodesForCleaner 核心逻辑
src/main/services/cleaner/cleaner-application-service.ts runCleaner 执行 ERP 清理
src/main/services/erp/cleaner.ts CleanerService.clean ERP 浏览器自动化

数据库层 (DAO)

文件 说明
src/main/services/database/materials-to-be-deleted-dao.ts MaterialsToBeDeletedDAO MaterialsToBeDeleted表操作
src/main/services/database/discrete-material-plan-dao.ts DiscreteMaterialPlanDAO 物料计划数据查询

数据流状态图

stateDiagram-v2
    [*] --> 物料录入:用户在 UI 输入物料
    物料录入 --> 待校验:保存到 MaterialsToBeDeleted 表

    待校验 --> 校验中:点击"数据校验"
    校验中 --> 已标记:通过关键词匹配负责人
    已标记 --> 待清理:用户勾选物料

    待清理 --> 清理执行中:点击"执行清理"
    清理执行中 --> 清理完成ERP 删除成功
    清理执行中 --> 部分失败:部分物料删除失败

    清理完成 --> [*]
    部分失败 --> [*]

    note right of 待清理
      Admin 可以查看和选择
      所有负责人的物料
    end note

    note right of 清理执行中
      从 MaterialsToBeDeleted 表
      根据 selectedManagers 过滤
      获取要删除的物料代码
    end note

数据流对比Admin vs 普通用户

graph TB
    subgraph "Admin 用户"
        A1[勾选多个 Manager]
        A2[SELECT FROM MaterialsToBeDeleted<br/>WHERE ManagerName IN selectedManagers]
        A3[获取所有选中负责人的物料]
    end

    subgraph "普通用户"
        B1[只能看到自己的物料]
        B2[SELECT FROM MaterialsToBeDeleted<br/>WHERE ManagerName = username]
        B3[只能删除自己的物料]
    end

    A1 --> A2 --> A3 --> Exec[执行清理]
    B1 --> B2 --> B3 --> Exec

常见问题解答

Q1: 物料代码是如何被录入到 MaterialsToBeDeleted 表的?

A: 有三种方式:

  1. 用户手动勾选 → 在 CleanerPage 勾选物料 → 点击"确认删除" → upsertBatch
  2. 关键词自动匹配 → 校验时根据 MaterialsTypeToBeDeleted 配置自动分配负责人和标记
  3. API 直接写入 → 其他服务调用 materials.upsertBatch IPC

Q2: Admin 如果不选 Manager 会怎样?

A: Admin 可以不选 Manager此时

  • 系统会尝试从"共享 Production ID"解析订单号
  • 然后从 DiscreteMaterialPlanData 表查询所有物料代码(不经过 MaterialsToBeDeleted 过滤)
  • 如果没有共享 Production ID则返回空数组无法执行清理

Q3: 物料代码会在清理后被自动删除吗?

A:

  • 干运行模式:不删除 ERP 数据,但会保留 UI 状态
  • 正式执行
    • ERP 中的物料计划被删除
    • MaterialsToBeDeleted 表中记录不会被自动删除(需要手动清理)

Q4: 如何清理已删除的物料记录?

A: Admin 可以在物料管理界面:

  • 按 Manager 筛选
  • 批量删除已处理的物料记录
  • 调用 materials.delete IPC 接口

总结

Admin 用户执行清理时,被清理物料的来源路径:

  1. 主要来源: dbo.MaterialsToBeDeleted
  2. 过滤条件: ManagerName IN (selectedManagers)
  3. 执行流程:
    • 从数据库查询物料代码
    • 结合订单号列表
    • 通过 Playwright 连接 ERP 系统
    • 逐个物料执行删除操作

关键点:被清理的物料必须先在 MaterialsToBeDeleted 表中存在记录,并且其 ManagerName 与 Admin 选择的负责人匹配。