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>
This commit is contained in:
Misaka_Company
2026-04-08 13:32:55 +08:00
parent 57b797196c
commit 1351371bd2
5 changed files with 911 additions and 0 deletions

482
demo_scripts/demo.md Normal file
View File

@@ -0,0 +1,482 @@
# Admin 用户物料清理数据流说明文档
## 概述
本文档详细说明 ERPAuto 系统中Admin 用户执行物料清理操作时,被清理物料的完整获取流程、数据来源和处理链路。
## 核心结论
**Admin 用户清理的物料来源**:被清理的物料代码从数据库表 `dbo.MaterialsToBeDeleted` 中获取,根据 Admin 用户在 UI 界面选择的负责人Manager进行过滤。
---
## 数据流总览
```mermaid
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 删除)
```
---
## 架构分层
```mermaid
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 查询**
```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)
```mermaid
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` 表:
```mermaid
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
```sql
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 用户执行实际删除操作的核心流程:
```mermaid
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
```
**关键代码路径**
```typescript
// 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 删除执行
```mermaid
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 用户获取物料的完整路径:
```mermaid
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` | 物料计划数据查询 |
---
## 数据流状态图
```mermaid
stateDiagram-v2
[*] --> 物料录入:用户在 UI 输入物料
物料录入 --> 待校验:保存到 MaterialsToBeDeleted 表
待校验 --> 校验中:点击"数据校验"
校验中 --> 已标记:通过关键词匹配负责人
已标记 --> 待清理:用户勾选物料
待清理 --> 清理执行中:点击"执行清理"
清理执行中 --> 清理完成ERP 删除成功
清理执行中 --> 部分失败:部分物料删除失败
清理完成 --> [*]
部分失败 --> [*]
note right of 待清理
Admin 可以查看和选择
所有负责人的物料
end note
note right of 清理执行中
从 MaterialsToBeDeleted 表
根据 selectedManagers 过滤
获取要删除的物料代码
end note
```
---
## 数据流对比Admin vs 普通用户
```mermaid
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 选择的负责人匹配。