chore: run pretier format across project

- Format TypeScript source files
- Format documentation files
- Update eslint config formatting
This commit is contained in:
Misaka_Company
2026-04-14 14:03:58 +08:00
parent 4ce5b91340
commit 343cb24234
10 changed files with 519 additions and 184 deletions

View File

@@ -13,14 +13,14 @@ Extractor 已有成熟的数据库持久化模式(`ExtractorOperationHistory`
## 设计决策
| 决策项 | 选择 | 理由 |
|--------|------|------|
| 表结构 | 独立建表,不与 Extractor 共用 | Cleaner 数据结构差异大(双层、物料级详情),独立更清晰 |
| 记录粒度 | 执行 + 订单 + 物料三层 | 执行表存全局信息,订单表存订单汇总,物料表存操作明细 |
| 批次标识 | `BatchId`UUID与 Extractor 一致 | 标准、简洁,不需要嵌入时间戳 |
| 重试记录 | 不覆盖,每次尝试独立写入,用 `AttemptNumber` 区分 | 保留完整审计链,为后续智能跳过提供数据基础 |
| 报告文件 | 移除 Markdown 报告和 RustFS 上传 | 数据库完全替代报告相关代码CleanerReportGenerator、generateAndUploadReport删除 |
| 前端历史 | 独立 CleanerOperationHistoryModal复用 Extractor 的 UI 模式 | 放在 CleanerPage 上,与 Extractor 的"操作历史"按钮对齐 |
| 决策项 | 选择 | 理由 |
| -------- | ------------------------------------------------------------ | ----------------------------------------------------------------------------------- |
| 表结构 | 独立建表,不与 Extractor 共用 | Cleaner 数据结构差异大(双层、物料级详情),独立更清晰 |
| 记录粒度 | 执行 + 订单 + 物料三层 | 执行表存全局信息,订单表存订单汇总,物料表存操作明细 |
| 批次标识 | `BatchId`UUID与 Extractor 一致 | 标准、简洁,不需要嵌入时间戳 |
| 重试记录 | 不覆盖,每次尝试独立写入,用 `AttemptNumber` 区分 | 保留完整审计链,为后续智能跳过提供数据基础 |
| 报告文件 | 移除 Markdown 报告和 RustFS 上传 | 数据库完全替代报告相关代码CleanerReportGenerator、generateAndUploadReport删除 |
| 前端历史 | 独立 CleanerOperationHistoryModal复用 Extractor 的 UI 模式 | 放在 CleanerPage 上,与 Extractor 的"操作历史"按钮对齐 |
## 数据库表结构
@@ -32,25 +32,25 @@ Extractor 已有成熟的数据库持久化模式(`ExtractorOperationHistory`
一次清理操作(含重试)的全局信息。每次尝试一行记录。
| 列名 | 类型 | 说明 |
|------|------|------|
| ID | INT IDENTITY | 自增主键 |
| BatchId | UNIQUEIDENTIFIER | 批次 ID一次清理操作含重试共享 |
| AttemptNumber | INT | 第几次尝试1=首次2=外层重试) |
| UserId | INT | 操作用户 ID |
| Username | NVARCHAR(255) | 操作用户名 |
| OperationTime | DATETIME | 操作时间 |
| EndTime | DATETIME | 结束时间 |
| Status | NVARCHAR(50) | pending / success / failed / partial / crashed |
| IsDryRun | BIT | 是否模拟运行 |
| TotalOrders | INT | 订单总数 |
| OrdersProcessed | INT | 已处理订单数 |
| TotalMaterialsDeleted | INT | 总删除物料数 |
| TotalMaterialsSkipped | INT | 总跳过物料数 |
| TotalMaterialsFailed | INT | 总失败物料数 |
| TotalUncertainDeletions | INT | 总不确定删除数 |
| ErrorMessage | NVARCHAR(MAX) | 全局错误信息(如外层崩溃原因) |
| AppVersion | NVARCHAR(20) | 应用版本号 |
| 列名 | 类型 | 说明 |
| ----------------------- | ---------------- | ---------------------------------------------- |
| ID | INT IDENTITY | 自增主键 |
| BatchId | UNIQUEIDENTIFIER | 批次 ID一次清理操作含重试共享 |
| AttemptNumber | INT | 第几次尝试1=首次2=外层重试) |
| UserId | INT | 操作用户 ID |
| Username | NVARCHAR(255) | 操作用户名 |
| OperationTime | DATETIME | 操作时间 |
| EndTime | DATETIME | 结束时间 |
| Status | NVARCHAR(50) | pending / success / failed / partial / crashed |
| IsDryRun | BIT | 是否模拟运行 |
| TotalOrders | INT | 订单总数 |
| OrdersProcessed | INT | 已处理订单数 |
| TotalMaterialsDeleted | INT | 总删除物料数 |
| TotalMaterialsSkipped | INT | 总跳过物料数 |
| TotalMaterialsFailed | INT | 总失败物料数 |
| TotalUncertainDeletions | INT | 总不确定删除数 |
| ErrorMessage | NVARCHAR(MAX) | 全局错误信息(如外层崩溃原因) |
| AppVersion | NVARCHAR(20) | 应用版本号 |
### 2. `CleanerOrderHistory`(订单级)
@@ -58,20 +58,20 @@ Extractor 已有成熟的数据库持久化模式(`ExtractorOperationHistory`
每个订单在每次尝试中的执行结果。每个订单每次尝试一行记录。
| 列名 | 类型 | 说明 |
|------|------|------|
| ID | INT IDENTITY | 自增主键 |
| BatchId | UNIQUEIDENTIFIER | 关联执行表 BatchId |
| AttemptNumber | INT | 关联执行表 AttemptNumber |
| OrderNumber | NVARCHAR(255) | 订单号 |
| Status | NVARCHAR(50) | pending / success / failed |
| MaterialsDeleted | INT | 删除物料数 |
| MaterialsSkipped | INT | 跳过物料数 |
| MaterialsFailed | INT | 删除失败物料数 |
| UncertainDeletions | INT | 不确定删除数 |
| RetryCount | INT | 内层重试次数 |
| RetrySuccess | BIT | 内层重试是否成功 |
| ErrorMessage | NVARCHAR(MAX) | 错误信息 |
| 列名 | 类型 | 说明 |
| ------------------ | ---------------- | -------------------------- |
| ID | INT IDENTITY | 自增主键 |
| BatchId | UNIQUEIDENTIFIER | 关联执行表 BatchId |
| AttemptNumber | INT | 关联执行表 AttemptNumber |
| OrderNumber | NVARCHAR(255) | 订单号 |
| Status | NVARCHAR(50) | pending / success / failed |
| MaterialsDeleted | INT | 删除物料数 |
| MaterialsSkipped | INT | 跳过物料数 |
| MaterialsFailed | INT | 删除失败物料数 |
| UncertainDeletions | INT | 不确定删除数 |
| RetryCount | INT | 内层重试次数 |
| RetrySuccess | BIT | 内层重试是否成功 |
| ErrorMessage | NVARCHAR(MAX) | 错误信息 |
关联方式:`BatchId + AttemptNumber` 关联执行表。
@@ -81,19 +81,19 @@ Extractor 已有成熟的数据库持久化模式(`ExtractorOperationHistory`
每个物料在每次尝试中的操作明细。
| 列名 | 类型 | 说明 |
|------|------|------|
| ID | INT IDENTITY | 自增主键 |
| BatchId | UNIQUEIDENTIFIER | 关联执行表 BatchId |
| AttemptNumber | INT | 关联执行表 AttemptNumber |
| OrderNumber | NVARCHAR(255) | 所属订单号 |
| MaterialCode | NVARCHAR(255) | 物料代码 |
| MaterialName | NVARCHAR(255) | 物料名称 |
| RowNumber | INT | 行号 |
| Result | NVARCHAR(50) | deleted / skipped / failed / uncertain |
| Reason | NVARCHAR(MAX) | 跳过/失败原因 |
| AttemptCount | INT | 删除尝试次数 |
| FinalErrorCategory | NVARCHAR(50) | 最终错误分类 |
| 列名 | 类型 | 说明 |
| ------------------ | ---------------- | -------------------------------------- |
| ID | INT IDENTITY | 自增主键 |
| BatchId | UNIQUEIDENTIFIER | 关联执行表 BatchId |
| AttemptNumber | INT | 关联执行表 AttemptNumber |
| OrderNumber | NVARCHAR(255) | 所属订单号 |
| MaterialCode | NVARCHAR(255) | 物料代码 |
| MaterialName | NVARCHAR(255) | 物料名称 |
| RowNumber | INT | 行号 |
| Result | NVARCHAR(50) | deleted / skipped / failed / uncertain |
| Reason | NVARCHAR(MAX) | 跳过/失败原因 |
| AttemptCount | INT | 删除尝试次数 |
| FinalErrorCategory | NVARCHAR(50) | 最终错误分类 |
关联方式:`BatchId + AttemptNumber + OrderNumber` 关联订单表。
@@ -102,18 +102,21 @@ Extractor 已有成熟的数据库持久化模式(`ExtractorOperationHistory`
首次执行到第 80 个订单时崩溃,外层重试成功完成全部 211 个订单:
**CleanerExecution**
```
BatchId=uuid-1, Attempt=1, Status=crashed, TotalOrders=211, Processed=80, ...
BatchId=uuid-1, Attempt=2, Status=success, TotalOrders=211, Processed=211, ...
```
**CleanerOrderHistory**Attempt=1 中部分记录)
```
BatchId=uuid-1, Attempt=1, Order=SC001, Status=success, Deleted=5, Skipped=1
BatchId=uuid-1, Attempt=1, Order=SC080, Status=crashed, Error=查询超时
```
**CleanerOrderHistory**Attempt=2 中部分记录)
```
BatchId=uuid-1, Attempt=2, Order=SC001, Status=success, Deleted=5, Skipped=1
BatchId=uuid-1, Attempt=2, Order=SC080, Status=success, Deleted=3, Skipped=0
@@ -121,6 +124,7 @@ BatchId=uuid-1, Attempt=2, Order=SC211, Status=success, Deleted=2, Skipped=0
```
**CleanerMaterialDetail**SC080 在 Attempt=2 中的物料)
```
BatchId=uuid-1, Attempt=2, Order=SC080, Material=MAT-001, Result=deleted
BatchId=uuid-1, Attempt=2, Order=SC080, Material=MAT-002, Result=skipped, Reason=不可删除
@@ -218,14 +222,14 @@ BatchId=uuid-1, Attempt=2, Order=SC080, Material=MAT-002, Result=skipped, Reason
## 移除的概念
| 概念 | 原因 |
|------|------|
| ExecutionIdCLN-时间戳-随机) | 为文件名设计,数据库用 UUID |
| generateExecutionId() | 随 ExecutionId 一起移除 |
| CleanerReportGenerator | Markdown 报告生成器,被数据库替代 |
| generateAndUploadReport() | RustFS 上传链路,被数据库写入替代 |
| 报告文件名去重 | 数据库 UUID 天然唯一 |
| 重试覆盖旧报告 | 数据库保留所有尝试记录 |
| 概念 | 原因 |
| ------------------------------ | --------------------------------- |
| ExecutionIdCLN-时间戳-随机) | 为文件名设计,数据库用 UUID |
| generateExecutionId() | 随 ExecutionId 一起移除 |
| CleanerReportGenerator | Markdown 报告生成器,被数据库替代 |
| generateAndUploadReport() | RustFS 上传链路,被数据库写入替代 |
| 报告文件名去重 | 数据库 UUID 天然唯一 |
| 重试覆盖旧报告 | 数据库保留所有尝试记录 |
## 不涉及的部分

View File

@@ -13,6 +13,7 @@
## Task 1: 新增类型定义
**Files:**
- Create: `src/main/types/cleaner-history.types.ts`
**Step 1: 创建类型文件**
@@ -146,6 +147,7 @@ feat(cleaner): add type definitions for cleaner operation history
## Task 2: 新增 DAO 层
**Files:**
- Create: `src/main/services/database/cleaner-operation-history-dao.ts`
**Step 1: 创建 DAO 文件**
@@ -165,47 +167,101 @@ export class CleanerOperationHistoryDAO {
}
async insertExecution(input: InsertCleanerExecutionInput): Promise<boolean>
async updateExecutionStatus(batchId: string, attemptNumber: number, status: string, ordersProcessed: number, materialsDeleted: number, materialsSkipped: number, materialsFailed: number, uncertainDeletions: number, endTime: Date, errorMessage?: string): Promise<boolean>
async updateExecutionStatus(
batchId: string,
attemptNumber: number,
status: string,
ordersProcessed: number,
materialsDeleted: number,
materialsSkipped: number,
materialsFailed: number,
uncertainDeletions: number,
endTime: Date,
errorMessage?: string
): Promise<boolean>
// ===== 订单表 =====
private getOrderTableName(): string {
return this.getDialect().quoteTableName('ERPAuto', 'CleanerOrderHistory')
}
async insertOrderRecords(batchId: string, attemptNumber: number, orders: InsertOrderInput[]): Promise<boolean>
async updateOrderStatus(batchId: string, attemptNumber: number, orderNumber: string, status: string, materialsDeleted: number, materialsSkipped: number, materialsFailed: number, uncertainDeletions: number, retryCount: number, retrySuccess: boolean, errorMessage?: string): Promise<boolean>
async insertOrderRecords(
batchId: string,
attemptNumber: number,
orders: InsertOrderInput[]
): Promise<boolean>
async updateOrderStatus(
batchId: string,
attemptNumber: number,
orderNumber: string,
status: string,
materialsDeleted: number,
materialsSkipped: number,
materialsFailed: number,
uncertainDeletions: number,
retryCount: number,
retrySuccess: boolean,
errorMessage?: string
): Promise<boolean>
// ===== 物料表 =====
private getMaterialTableName(): string {
return this.getDialect().quoteTableName('ERPAuto', 'CleanerMaterialDetail')
}
async insertMaterialDetails(batchId: string, attemptNumber: number, details: InsertMaterialDetailInput[]): Promise<boolean>
async insertMaterialDetails(
batchId: string,
attemptNumber: number,
details: InsertMaterialDetailInput[]
): Promise<boolean>
// ===== 查询 =====
async getBatches(userId?: number, options?: GetCleanerBatchesOptions): Promise<CleanerBatchStats[]>
async getBatchDetails(batchId: string): Promise<{ executions: CleanerExecutionRecord[]; orders: CleanerOrderRecord[] }>
async getMaterialDetails(batchId: string, attemptNumber: number, orderNumber: string): Promise<CleanerMaterialRecord[]>
async getBatches(
userId?: number,
options?: GetCleanerBatchesOptions
): Promise<CleanerBatchStats[]>
async getBatchDetails(
batchId: string
): Promise<{ executions: CleanerExecutionRecord[]; orders: CleanerOrderRecord[] }>
async getMaterialDetails(
batchId: string,
attemptNumber: number,
orderNumber: string
): Promise<CleanerMaterialRecord[]>
// ===== 删除 =====
async deleteBatch(batchId: string, requestingUserId: number, isAdmin: boolean): Promise<{ success: boolean; error?: string }>
async deleteBatch(
batchId: string,
requestingUserId: number,
isAdmin: boolean
): Promise<{ success: boolean; error?: string }>
// ===== 列询执行级记录 =====
async getMaterialDetails(batchId: string, attemptNumber: number, orderNumber: string): Promise<CleanerMaterialRecord[]>
async getMaterialDetails(
batchId: string,
attemptNumber: number,
orderNumber: string
): Promise<CleanerMaterialRecord[]>
// ===== 删除 =====
async deleteBatch(batchId: string, requestingUserId: number, isAdmin: boolean): Promise<{ success: boolean; error?: string }>
async deleteBatch(
batchId: string,
requestingUserId: number,
isAdmin: boolean
): Promise<{ success: boolean; error?: string }>
async disconnect(): Promise<void>
}
```
`getBatches` 查询逻辑:
- `GROUP BY BatchId`,取 `MAX(AttemptNumber)` 对应的执行记录状态作为最终状态
- 汇总订单级的 success/failed 计数
- 支持 userId 过滤(普通用户)和 usernames 过滤(管理员)
- 支持分页
`getBatchDetails` 查询逻辑:
- 返回某 BatchId 下所有 execution 记录 + order 记录
- 前端用 attemptNumber 区分不同尝试
@@ -227,6 +283,7 @@ feat(cleaner): add CleanerOperationHistoryDAO for three-table persistence
## Task 3: 新增 IPC channels
**Files:**
- Modify: `src/shared/ipc-channels.ts`
**Step 1: 添加 cleaner history channels**
@@ -252,6 +309,7 @@ feat(cleaner): add IPC channels for cleaner operation history
## Task 4: 新增 IPC handler
**Files:**
- Create: `src/main/ipc/cleaner-history-handler.ts`
- Modify: `src/main/ipc/index.ts` — 注册新 handler
@@ -282,14 +340,24 @@ export function registerCleanerHistoryHandlers(): void {
ipcMain.handle(
IPC_CHANNELS.CLEANER_HISTORY_GET_BATCH_DETAILS,
async (event, batchId: string): Promise<IpcResult<{ executions: CleanerExecutionRecord[]; orders: CleanerOrderRecord[] }>> => {
async (
event,
batchId: string
): Promise<
IpcResult<{ executions: CleanerExecutionRecord[]; orders: CleanerOrderRecord[] }>
> => {
// ... 与 operation-history-handler 的 getBatchDetails 模式一致
}
)
ipcMain.handle(
IPC_CHANNELS.CLEANER_HISTORY_GET_MATERIAL_DETAILS,
async (event, batchId: string, attemptNumber: number, orderNumber: string): Promise<IpcResult<CleanerMaterialRecord[]>> => {
async (
event,
batchId: string,
attemptNumber: number,
orderNumber: string
): Promise<IpcResult<CleanerMaterialRecord[]>> => {
// ...
}
)
@@ -323,6 +391,7 @@ feat(cleaner): add IPC handlers for cleaner operation history
## Task 5: 新增 Preload API
**Files:**
- Modify: `src/preload/api/cleaner.ts` — 新增 history 方法
- Modify: `src/preload/index.d.ts` — 新增类型声明
@@ -374,6 +443,7 @@ feat(cleaner): add preload API for cleaner operation history
## Task 6: 改造 CleanerApplicationService — 写入数据库记录
**Files:**
- Modify: `src/main/services/cleaner/cleaner-application-service.ts`
这是核心变更。`runCleaner` 方法需要:
@@ -403,6 +473,7 @@ async runCleaner(
**Step 4: 执行后更新订单记录和写入物料明细**
清理完成后遍历 `result.details``OrderCleanDetail[]`),对每个订单:
- 调用 `historyDao.updateOrderStatus(...)` 更新订单结果
- 调用 `historyDao.insertMaterialDetails(...)` 写入物料明细skipped + failed 材料全部写入)
@@ -413,6 +484,7 @@ async runCleaner(
**Step 6: 外层重试改造**
`result.crashed` 时:
1. 调用 `historyDao.updateExecutionStatus(batchId, 1, 'crashed', ...)` 标记首次尝试为 crashed
2. 调用 `historyDao.insertExecution({ batchId, attemptNumber: 2, ... })` 创建第二次尝试
3. 调用 `historyDao.insertOrderRecords(batchId, 2, orders)` 写入第二次尝试的 pending 订单
@@ -435,11 +507,13 @@ refactor(cleaner): replace report generation with database persistence
## Task 7: 改造 cleaner-handler.ts — 执行前后写入
**Files:**
- Modify: `src/main/ipc/cleaner-handler.ts`
**Step 1: 修改 CLEANER_RUN handler**
在调用 `cleanerService.runCleaner()` 之前:
1. 获取当前用户信息
2. `batchId = randomUUID()`
3. 创建 `CleanerOperationHistoryDAO` 实例
@@ -469,6 +543,7 @@ refactor(cleaner): write execution records to database in IPC handler
## Task 8: 删除 Markdown 报告生成器
**Files:**
- Delete: `src/main/services/report/cleaner-report-generator.ts`
**Step 1: 删除文件**
@@ -495,6 +570,7 @@ refactor(cleaner): remove Markdown report generator
## Task 9: 前端 — 新增操作历史弹窗
**Files:**
- Create: `src/renderer/src/components/CleanerOperationHistoryModal.tsx`
- Modify: `src/renderer/src/pages/CleanerPage.tsx`
@@ -531,6 +607,7 @@ feat(cleaner): add operation history modal with database-backed records
## Task 10: 更新 renderer 类型定义
**Files:**
- Modify: `src/renderer/src/hooks/cleaner/types.ts`
**Step 1: 添加 history 相关类型**
@@ -553,6 +630,7 @@ feat(cleaner): add renderer types for cleaner operation history
## Task 11: 清理旧代码
**Files:**
- Modify: `src/renderer/src/hooks/cleaner/types.ts` — 移除 `CleanerReportData.crashed`(如果不再需要)
- 检查 `ReportViewerDialog.tsx``ReportAnalysisDialog.tsx` 是否仍被 Cleaner 使用
@@ -564,6 +642,7 @@ feat(cleaner): add renderer types for cleaner operation history
**Step 2: 评估 ReportViewerDialog 和 ReportAnalysisDialog**
这两个组件目前用于查看 Markdown 报告文件。如果 Cleaner 不再使用它们:
- 在 CleanerPage 中移除相关按钮和引用
- 不删除组件本身Extractor 可能仍在使用,后续统一清理)

View File

@@ -8,13 +8,13 @@
## 设计决策
| 决策项 | 选择 | 理由 |
|---|---|---|
| 重试层级 | CleanerApplicationService | 崩溃后浏览器不可用,必须重新登录 |
| 重试范围 | 全部订单重新跑 | 简单可靠,物料删除是幂等操作 |
| 最大重试次数 | 1 次 | 覆盖瞬态故障,不过度消耗时间 |
| 触发条件 | result.crashed === true | 仅 outer catch 触发时才重试 |
| 报告去重 | 执行 ID | 用户点击执行时生成,重试不变 |
| 决策项 | 选择 | 理由 |
| ------------ | ------------------------- | -------------------------------- |
| 重试层级 | CleanerApplicationService | 崩溃后浏览器不可用,必须重新登录 |
| 重试范围 | 全部订单重新跑 | 简单可靠,物料删除是幂等操作 |
| 最大重试次数 | 1 次 | 覆盖瞬态故障,不过度消耗时间 |
| 触发条件 | result.crashed === true | 仅 outer catch 触发时才重试 |
| 报告去重 | 执行 ID | 用户点击执行时生成,重试不变 |
## 变更清单
@@ -25,7 +25,7 @@
```typescript
export interface CleanerResult {
// ... 现有字段
crashed?: boolean // true = outer catch triggered, 流程级崩溃
crashed?: boolean // true = outer catch triggered, 流程级崩溃
}
```
@@ -87,6 +87,7 @@ runCleaner(eventSender, input) {
生成时机: `runCleaner()` 入口处,在 ERP 登录之前。重试时同一个 executionId 不变。
用途:
- 报告文件名: `cleaner-report-CLN-20260410112930-A7FK.md`
- RustFS 存储路径中包含该 ID重试时覆盖同一文件
- 报告内容中显示该 ID
@@ -98,13 +99,13 @@ runCleaner(eventSender, input) {
在执行摘要表格中新增字段:
```markdown
| 项目 | 值 |
| ---------------- | --------------------------------- |
| **执行 ID** | `CLN-20260410112930-A7FK` | ← 新增
| **应用版本** | `1.11.1` | ← 新增
| **执行时间** | `2026-04-10 11:29:30` |
| **执行模式** | `正式执行` |
| ... | ... |
| 项目 | 值 |
| ------------ | ------------------------- | ------ |
| **执行 ID** | `CLN-20260410112930-A7FK` | ← 新增 |
| **应用版本** | `1.11.1` | ← 新增 |
| **执行时间** | `2026-04-10 11:29:30` |
| **执行模式** | `正式执行` |
| ... | ... |
```
- **执行 ID**: 从 ReportOptions 传入
@@ -118,8 +119,8 @@ export interface ReportOptions {
username: string
startTime: number
endTime: number
executionId: string // ← 新增
appVersion: string // ← 新增
executionId: string // ← 新增
appVersion: string // ← 新增
}
```