feat(mid-section-defect): full-line bidirectional defect trace center with dual query mode
Transform /mid-section-defect from TMTT-only backward analysis into a full-line bidirectional defect traceability center supporting all detection stations. Key changes: - Parameterized station detection: any workcenter group as detection station - Bidirectional tracing: backward (upstream attribution) + forward (downstream reject rates) - Dual query mode: date range OR LOT/工單/WAFER container-based seed resolution - Multi-select filters for upstream station, equipment model (RESOURCEFAMILYNAME), and loss reasons - Progressive 3-stage trace pipeline (seed-resolve → lineage → events) with streaming UI - Equipment model lookup via resource cache instead of SPECNAME - Session caching, auto-refresh, searchable MultiSelect with fuzzy matching - Remove legacy tmtt-defect module (fully superseded) - Archive openspec change artifacts Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-02-23
|
||||
@@ -0,0 +1,75 @@
|
||||
## Context
|
||||
|
||||
`/mid-section-defect` currently runs a 3-stage backward-only pipeline hardcoded to TMTT (測試) station:
|
||||
1. `tmtt_detection.sql` — fetch defective lots at TMTT station
|
||||
2. `LineageEngine.resolve_full_genealogy()` — find ancestor container IDs
|
||||
3. `upstream_history.sql` — get WIP records at upstream stations → attribute defects to machines
|
||||
|
||||
The detection SQL has `LIKE '%TMTT%'` hardcoded on line 38. All internal naming uses `TMTT_` prefix. The page serves one direction (backward) for one station.
|
||||
|
||||
This change generalizes to any of the 12 workcenter groups as detection station, adds forward tracing direction, and removes the superseded `/tmtt-defect` page.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- Parameterize detection station: replace TMTT hardcode with `{{ STATION_FILTER }}` built from `workcenter_groups.py` patterns
|
||||
- Add forward tracing pipeline: detection rejects → forward lineage → downstream WIP + rejects → forward attribution
|
||||
- Direction-aware UI: FilterBar station dropdown + direction toggle, KPI/charts/detail switch by direction
|
||||
- Backward compatibility: `station=測試, direction=backward` produces identical results (renamed columns)
|
||||
- Remove `/tmtt-defect` page and all associated code
|
||||
|
||||
**Non-Goals:**
|
||||
- No changes to LineageEngine internals (already supports both `resolve_full_genealogy` and `resolve_forward_tree`)
|
||||
- No changes to `reject-history` or `query-tool` pages
|
||||
- No new caching strategy (reuse existing L1/L2 cache with station+direction in key)
|
||||
- No multi-station or multi-direction in a single query
|
||||
|
||||
## Decisions
|
||||
|
||||
### D1: Parameterized SQL via template substitution (not dynamic SQL builder)
|
||||
|
||||
Use `SQLLoader.load_with_params()` with `{{ STATION_FILTER }}` placeholder — the same pattern already used by `upstream_history.sql`'s `{{ ANCESTOR_FILTER }}`. The filter is built in Python from `WORKCENTER_GROUPS[station]['patterns']` as OR-LIKE clauses with bind parameters.
|
||||
|
||||
**Alternative considered:** Dynamic SQL builder class. Rejected — adds abstraction for a simple OR-LIKE pattern; template substitution is established in the codebase.
|
||||
|
||||
### D2: Separate `station_detection.sql` instead of modifying `tmtt_detection.sql`
|
||||
|
||||
Create new `station_detection.sql` as a generalized copy. The old `tmtt_detection.sql` will be deleted when `/tmtt-defect` is removed. Clean separation avoids merge conflicts with any in-flight tmtt-defect work.
|
||||
|
||||
**Alternative considered:** Modify in-place. Rejected — the old file is deleted anyway and renaming avoids ambiguity.
|
||||
|
||||
### D3: Forward attribution uses TRACKINQTY as denominator
|
||||
|
||||
Forward reject rate = `REJECT_TOTAL_QTY / TRACKINQTY × 100` at each downstream station. TRACKINQTY comes from `upstream_history.sql` (needs adding to SELECT). This gives a per-station defect rate for lots that survived the detection station.
|
||||
|
||||
**Alternative considered:** Use lot count as denominator. Rejected — TRACKINQTY accounts for partial quantities (split/merge lots) and gives a more accurate rate.
|
||||
|
||||
### D4: Direction dispatch at service layer, not route layer
|
||||
|
||||
`query_analysis()` gains `station` and `direction` params and dispatches to `_run_backward_pipeline()` or `_run_forward_pipeline()` internally. Routes just pass through. This keeps route handlers thin and testable.
|
||||
|
||||
### D5: Forward pipeline reuses upstream_history.sql for WIP records
|
||||
|
||||
Both directions need WIP records at various stations. The existing `upstream_history.sql` (with added TRACKINQTY) serves both — just with different container ID sets (ancestors for backward, descendants for forward).
|
||||
|
||||
### D6: New `downstream_rejects` event domain in EventFetcher
|
||||
|
||||
Forward tracing needs reject records at downstream stations. Add `downstream_rejects` as a new domain in `EventFetcher._build_domain_sql()`, loading `downstream_rejects.sql` with batched IN clause. This follows the established domain pattern.
|
||||
|
||||
### D7: Frontend direction toggle — button group, not dropdown
|
||||
|
||||
Two discrete states (backward/forward) fit a toggle button group better than a dropdown. Matches the existing btn-primary pattern in the page's CSS.
|
||||
|
||||
### D8: Remove `/tmtt-defect` entirely
|
||||
|
||||
The generalized traceability center with `station=測試 + lossReasons=[276_腳型不良, 277_印字不良]` reproduces all tmtt-defect functionality. Remove: `frontend/src/tmtt-defect/`, backend routes/services/SQL, test files, and `nativeModuleRegistry.js` registration.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- **Forward pipeline performance for early stations** — Selecting `station=切割 (order=0), direction=forward` could produce a very large descendant tree (all lots flow downstream). → Mitigation: The existing `resolve_forward_tree()` already handles large sets; add a result count warning in UI if > 5000 tracked lots.
|
||||
|
||||
- **TRACKINQTY NULL values** — Some WIP records may have NULL TRACKINQTY. → Mitigation: COALESCE to 0 in SQL; skip lots with zero input in attribution to avoid division by zero.
|
||||
|
||||
- **TMTT removal breaks bookmarks** — Users with `/tmtt-defect` bookmarks get 404. → Mitigation: Low risk — page was in dev status, not released. No redirect needed.
|
||||
|
||||
- **Rename TMTT_ → DETECTION_ in API response keys** — Frontend consumers (CSV export, chart keys) reference these field names. → Mitigation: All consumers are within this page's code; rename consistently in one pass.
|
||||
@@ -0,0 +1,32 @@
|
||||
## Why
|
||||
|
||||
`/mid-section-defect` 目前僅支援 TMTT 測試站的反向不良追溯,偵測站硬編碼在 SQL 中。實務上需要從任意站點偵測不良並雙向追溯:後段不良回推上游集中機台(反向),前段報廢後倖存批次的下游表現(正向)。將此頁面升級為全線雙向追溯中心,覆蓋 12 個 workcenter group × 2 方向的分析需求。同時移除功能被完全取代的 `/tmtt-defect`(TMTT印字腳型不良分析)頁面。
|
||||
|
||||
## What Changes
|
||||
|
||||
- **偵測站泛化**:將硬編碼的 TMTT 站點篩選改為參數化,使用者可從 12 個 workcenter group 中選擇任意偵測站
|
||||
- **反向追溯泛化**:現有 TMTT → 上游機台歸因邏輯保留,但偵測站改為可選(預設仍為「測試」)
|
||||
- **新增正向追溯**:偵測站報廢批次 → 追蹤倖存批次往下游走 → 各下游站的額外報廢率(判斷部分報廢後剩餘品是否仍有問題)
|
||||
- **UI 改版**:FilterBar 新增偵測站下拉 + 方向切換;KPI/圖表/明細表依方向動態切換
|
||||
- **重新命名**:頁面標題從「中段製程不良追溯」改為「製程不良追溯分析」,內部 TMTT_ 前綴統一改為 DETECTION_
|
||||
- **移除 TMTT 印字腳型不良分析**:`/tmtt-defect` 頁面功能已被泛化後的追溯中心完全覆蓋(選偵測站=測試 + 篩選不良原因=276_腳型不良/277_印字不良),移除前後端代碼與路由註冊
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
- `defect-trace-station-detection`: 參數化偵測站 SQL 與篩選邏輯,支援任意 workcenter group 作為偵測起點
|
||||
- `defect-trace-forward-pipeline`: 正向追溯 pipeline — 偵測站報廢批次 → forward lineage → 下游 WIP + 下游報廢記錄 → 正向歸因引擎
|
||||
- `defect-trace-bidirectional-ui`: 雙向追溯前端 — 偵測站選擇器、方向切換、方向感知的 KPI/圖表/明細表/CSV 匯出
|
||||
|
||||
### Modified Capabilities
|
||||
- `progressive-trace-ux`: 需擴展支援 direction 參數,lineage stage 依方向選擇 ancestor 或 forward tree
|
||||
- `event-fetcher-unified`: 新增 `downstream_rejects` event domain
|
||||
|
||||
## Impact
|
||||
|
||||
- **Backend**: `mid_section_defect_service.py`(主要重構)、`mid_section_defect_routes.py`、`trace_routes.py`、`event_fetcher.py`
|
||||
- **SQL**: 新增 `station_detection.sql`、`downstream_rejects.sql`;修改 `upstream_history.sql`(加 TRACKINQTY)
|
||||
- **Frontend**: `FilterBar.vue`、`App.vue`、`KpiCards.vue`、`DetailTable.vue`、`useTraceProgress.js`、`style.css`
|
||||
- **Config**: `page_status.json`(頁面名稱更新 + 移除 tmtt-defect 條目)
|
||||
- **API**: 所有 `/api/mid-section-defect/*` 端點新增 `station` + `direction` 參數;新增 `/station-options` 端點
|
||||
- **移除**: `frontend/src/tmtt-defect/`(整個目錄)、`src/mes_dashboard/routes/tmtt_defect_routes.py`、`src/mes_dashboard/services/tmtt_defect_service.py`、`src/mes_dashboard/sql/tmtt_defect/`、相關測試檔案、`nativeModuleRegistry.js` 中的 tmtt-defect 註冊
|
||||
@@ -0,0 +1,94 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: FilterBar SHALL include station dropdown
|
||||
FilterBar SHALL display a `<select>` dropdown populated from `GET /api/mid-section-defect/station-options` on mount. The dropdown SHALL default to '測試' and emit `station` via the `update-filters` mechanism.
|
||||
|
||||
#### Scenario: Station dropdown loads on mount
|
||||
- **WHEN** the FilterBar component mounts
|
||||
- **THEN** it SHALL fetch station options from the API and populate the dropdown with 12 workcenter groups
|
||||
- **THEN** the default selection SHALL be '測試'
|
||||
|
||||
#### Scenario: Station selection updates filters
|
||||
- **WHEN** user selects a different station
|
||||
- **THEN** `update-filters` SHALL emit with the new `station` value
|
||||
|
||||
### Requirement: FilterBar SHALL include direction toggle
|
||||
FilterBar SHALL display a toggle button group with two options: '反向追溯' (`backward`) and '正向追溯' (`forward`). Default SHALL be `backward`.
|
||||
|
||||
#### Scenario: Direction toggle switches direction
|
||||
- **WHEN** user clicks '正向追溯'
|
||||
- **THEN** `update-filters` SHALL emit with `direction: 'forward'`
|
||||
- **THEN** the active button SHALL visually indicate the selected direction
|
||||
|
||||
### Requirement: KPI cards SHALL display direction-aware labels
|
||||
KpiCards component SHALL accept `direction` and `stationLabel` props and switch card labels between backward and forward modes.
|
||||
|
||||
#### Scenario: Backward KPI labels
|
||||
- **WHEN** `direction='backward'`
|
||||
- **THEN** KPI cards SHALL display existing labels: 偵測批次數, 偵測不良數, 上游追溯批次數, 上游站點數, etc.
|
||||
|
||||
#### Scenario: Forward KPI labels
|
||||
- **WHEN** `direction='forward'`
|
||||
- **THEN** KPI cards SHALL display: 偵測批次數, 偵測不良數, 追蹤批次數, 下游到達站數, 下游不良總數, 下游不良率
|
||||
|
||||
### Requirement: Chart layout SHALL switch by direction
|
||||
App.vue SHALL render direction-appropriate chart sets.
|
||||
|
||||
#### Scenario: Backward chart layout
|
||||
- **WHEN** `direction='backward'`
|
||||
- **THEN** SHALL render 6 Pareto charts: by_station, by_loss_reason, by_machine, by_detection_machine, by_workflow, by_package
|
||||
|
||||
#### Scenario: Forward chart layout
|
||||
- **WHEN** `direction='forward'`
|
||||
- **THEN** SHALL render 4 Pareto charts: by_downstream_station, by_downstream_loss_reason, by_downstream_machine, by_detection_machine
|
||||
|
||||
### Requirement: Detail table columns SHALL switch by direction
|
||||
DetailTable component SHALL accept a `direction` prop and render direction-appropriate columns.
|
||||
|
||||
#### Scenario: Backward detail columns
|
||||
- **WHEN** `direction='backward'`
|
||||
- **THEN** columns SHALL match existing backward layout (CONTAINERID, station history, upstream machine attribution, etc.)
|
||||
|
||||
#### Scenario: Forward detail columns
|
||||
- **WHEN** `direction='forward'`
|
||||
- **THEN** columns SHALL include: CONTAINERID, 偵測設備, 偵測投入, 偵測不良, 下游到達站數, 下游不良總數, 下游不良率, 最差下游站
|
||||
|
||||
### Requirement: Page header SHALL reflect station and direction
|
||||
Page title SHALL be '製程不良追溯分析'. Subtitle SHALL dynamically reflect station and direction.
|
||||
|
||||
#### Scenario: Backward subtitle
|
||||
- **WHEN** `station='電鍍', direction='backward'`
|
||||
- **THEN** subtitle SHALL indicate: `電鍍站不良 → 回溯上游機台歸因`
|
||||
|
||||
#### Scenario: Forward subtitle
|
||||
- **WHEN** `station='成型', direction='forward'`
|
||||
- **THEN** subtitle SHALL indicate: `成型站不良批次 → 追蹤倖存批次下游表現`
|
||||
|
||||
### Requirement: CSV export SHALL include direction-appropriate columns
|
||||
Export SHALL produce CSV with columns matching the current direction's detail table.
|
||||
|
||||
#### Scenario: Forward CSV export
|
||||
- **WHEN** user exports with `direction='forward'`
|
||||
- **THEN** CSV SHALL contain forward-specific columns (detection equipment, downstream stats)
|
||||
|
||||
### Requirement: Page metadata SHALL be updated
|
||||
`page_status.json` SHALL update the page name from '中段製程不良追溯' to '製程不良追溯分析'.
|
||||
|
||||
#### Scenario: Page name in page_status.json
|
||||
- **WHEN** the page metadata is read
|
||||
- **THEN** the name for `mid-section-defect` SHALL be '製程不良追溯分析'
|
||||
|
||||
## REMOVED Requirements
|
||||
|
||||
### Requirement: TMTT印字腳型不良分析 page
|
||||
**Reason**: Functionality fully superseded by generalized traceability center (station=測試 + loss reasons filter for 276_腳型不良/277_印字不良)
|
||||
**Migration**: Use `/mid-section-defect` with station=測試 and filter loss reasons to 276_腳型不良 or 277_印字不良
|
||||
|
||||
#### Scenario: TMTT defect page removal
|
||||
- **WHEN** the change is complete
|
||||
- **THEN** `frontend/src/tmtt-defect/` directory SHALL be removed
|
||||
- **THEN** `src/mes_dashboard/routes/tmtt_defect_routes.py` SHALL be removed
|
||||
- **THEN** `src/mes_dashboard/services/tmtt_defect_service.py` SHALL be removed
|
||||
- **THEN** `src/mes_dashboard/sql/tmtt_defect/` directory SHALL be removed
|
||||
- **THEN** `nativeModuleRegistry.js` SHALL have tmtt-defect registration removed
|
||||
- **THEN** `page_status.json` SHALL have tmtt-defect entry removed
|
||||
@@ -0,0 +1,76 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Forward pipeline SHALL trace surviving lots downstream
|
||||
When `direction=forward`, the system SHALL execute a forward tracing pipeline: detection station rejects → forward lineage (descendants) → downstream WIP + downstream rejects → forward attribution engine.
|
||||
|
||||
#### Scenario: Forward pipeline stages
|
||||
- **WHEN** `query_analysis(station='成型', direction='forward')` is called
|
||||
- **THEN** the pipeline SHALL execute in order:
|
||||
1. Fetch detection data at 成型 station (lots with rejects in date range)
|
||||
2. Resolve forward lineage via `LineageEngine.resolve_forward_tree(detection_cids)`
|
||||
3. Collect tracked CIDs = detection CIDs ∪ all descendants
|
||||
4. Fetch WIP history for tracked CIDs (with TRACKINQTY)
|
||||
5. Fetch downstream reject records for tracked CIDs
|
||||
6. Run forward attribution engine
|
||||
7. Build KPI, charts, detail table, and trend data
|
||||
|
||||
#### Scenario: No descendants found
|
||||
- **WHEN** forward lineage returns an empty descendants map
|
||||
- **THEN** KPI SHALL show zero downstream rejects and zero downstream stations reached
|
||||
- **THEN** charts and detail table SHALL be empty arrays
|
||||
|
||||
### Requirement: downstream_rejects.sql SHALL query reject records for tracked lots
|
||||
`downstream_rejects.sql` SHALL query `DW_MES_LOTREJECTHISTORY` for batched CONTAINERIDs with the standard `WORKCENTER_GROUP` CASE WHEN classification.
|
||||
|
||||
#### Scenario: Downstream rejects query output columns
|
||||
- **WHEN** the SQL is executed
|
||||
- **THEN** it SHALL return: `CONTAINERID`, `WORKCENTERNAME`, `WORKCENTER_GROUP`, `LOSSREASONNAME`, `EQUIPMENTNAME`, `REJECT_TOTAL_QTY`, `TXNDATE`
|
||||
|
||||
#### Scenario: Batched IN clause for large CID sets
|
||||
- **WHEN** tracked CIDs exceed 1000
|
||||
- **THEN** the system SHALL batch queries in groups of 1000 (same pattern as `upstream_history.sql`)
|
||||
|
||||
### Requirement: upstream_history.sql SHALL include TRACKINQTY
|
||||
The `upstream_history.sql` query SHALL include `h.TRACKINQTY` in both the `ranked_history` CTE and the final SELECT output.
|
||||
|
||||
#### Scenario: TRACKINQTY in output
|
||||
- **WHEN** the SQL is executed
|
||||
- **THEN** each row SHALL include `TRACKINQTY` representing the input quantity at that station
|
||||
- **THEN** NULL values SHALL be handled as 0 via COALESCE
|
||||
|
||||
### Requirement: Forward attribution engine SHALL compute per-station reject rates
|
||||
The forward attribution engine SHALL aggregate reject data by downstream station (stations with order > detection station's order) and compute reject rates using TRACKINQTY as denominator.
|
||||
|
||||
#### Scenario: Forward attribution calculation
|
||||
- **WHEN** tracked lots reach downstream station Y with total TRACKINQTY=1000 and REJECT_TOTAL_QTY=50
|
||||
- **THEN** station Y's reject rate SHALL be `50 / 1000 × 100 = 5.0%`
|
||||
|
||||
#### Scenario: Only downstream stations included
|
||||
- **WHEN** detection station is 成型 (order=4)
|
||||
- **THEN** attribution SHALL only include stations with order > 4 (去膠, 水吹砂, 電鍍, 移印, 切彎腳, 元件切割, 測試)
|
||||
- **THEN** stations with order ≤ 4 SHALL be excluded from forward attribution
|
||||
|
||||
#### Scenario: Zero input quantity guard
|
||||
- **WHEN** a downstream station has TRACKINQTY sum = 0 for tracked lots
|
||||
- **THEN** reject rate SHALL be 0 (not division error)
|
||||
|
||||
### Requirement: Forward KPI SHALL summarize downstream impact
|
||||
Forward direction KPI SHALL include: detection lot count, detection defect quantity, tracked lot count (detection + descendants), downstream stations reached, downstream total rejects, and overall downstream reject rate.
|
||||
|
||||
#### Scenario: Forward KPI fields
|
||||
- **WHEN** forward analysis completes
|
||||
- **THEN** KPI SHALL contain `detection_lot_count`, `detection_defect_qty`, `tracked_lot_count`, `downstream_stations_reached`, `downstream_total_reject`, `downstream_reject_rate`
|
||||
|
||||
### Requirement: Forward charts SHALL show downstream distribution
|
||||
Forward direction charts SHALL include: by_downstream_station (Pareto by station reject qty), by_downstream_machine (Pareto by equipment), by_downstream_loss_reason (Pareto by reason), by_detection_machine (Pareto by detection station equipment).
|
||||
|
||||
#### Scenario: Forward chart keys
|
||||
- **WHEN** forward analysis completes
|
||||
- **THEN** charts SHALL contain keys: `by_downstream_station`, `by_downstream_machine`, `by_downstream_loss_reason`, `by_detection_machine`
|
||||
|
||||
### Requirement: Forward detail table SHALL show per-lot downstream tracking
|
||||
Forward direction detail table SHALL show one row per detection lot with downstream tracking summary.
|
||||
|
||||
#### Scenario: Forward detail columns
|
||||
- **WHEN** forward detail is requested
|
||||
- **THEN** each row SHALL include: CONTAINERID, DETECTION_EQUIPMENTNAME, TRACKINQTY (at detection), detection reject qty, downstream stations reached count, downstream total rejects, downstream reject rate, worst downstream station (highest reject rate)
|
||||
@@ -0,0 +1,53 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Detection SQL SHALL be parameterized by workcenter group
|
||||
The system SHALL replace hardcoded TMTT station filtering with a `{{ STATION_FILTER }}` template placeholder in `station_detection.sql`. The filter SHALL be built from `WORKCENTER_GROUPS[station]['patterns']` and `['exclude']` defined in `workcenter_groups.py`, generating OR-LIKE clauses with bind parameters.
|
||||
|
||||
#### Scenario: Station filter built from workcenter group patterns
|
||||
- **WHEN** `station='電鍍'` is requested
|
||||
- **THEN** the system SHALL build a SQL fragment: `UPPER(h.WORKCENTERNAME) LIKE :wc_p0 OR UPPER(h.WORKCENTERNAME) LIKE :wc_p1 OR ...` with bind values `['%掛鍍%', '%滾鍍%', '%條鍍%', '%電鍍%', '%補鍍%', '%TOTAI%', '%BANDL%']`
|
||||
|
||||
#### Scenario: Station filter respects exclude patterns
|
||||
- **WHEN** `station='切割'` is requested (which has `exclude: ['元件切割', 'PKG_SAW']`)
|
||||
- **THEN** the filter SHALL include patterns for '切割' AND exclude patterns via `AND UPPER(h.WORKCENTERNAME) NOT LIKE :wc_ex0 AND NOT LIKE :wc_ex1`
|
||||
|
||||
#### Scenario: Default station is 測試
|
||||
- **WHEN** no `station` parameter is provided
|
||||
- **THEN** the system SHALL default to `station='測試'` (patterns: `['TMTT', '測試']`)
|
||||
- **THEN** results SHALL be equivalent to the previous hardcoded TMTT behavior
|
||||
|
||||
### Requirement: station_detection.sql SHALL generalize tmtt_detection.sql
|
||||
`station_detection.sql` SHALL be a new SQL file that replaces `tmtt_detection.sql` with parameterized station filtering. Column aliases SHALL use `DETECTION_` prefix instead of `TMTT_` prefix.
|
||||
|
||||
#### Scenario: SQL column renaming
|
||||
- **WHEN** `station_detection.sql` is executed
|
||||
- **THEN** output columns SHALL include `DETECTION_EQUIPMENTID` and `DETECTION_EQUIPMENTNAME` (not `TMTT_EQUIPMENTID` / `TMTT_EQUIPMENTNAME`)
|
||||
|
||||
#### Scenario: Both WIP and reject CTEs use station filter
|
||||
- **WHEN** the SQL is executed
|
||||
- **THEN** both the WIP history CTE and the reject history CTE SHALL apply `{{ STATION_FILTER }}` to filter by the selected station
|
||||
|
||||
### Requirement: Station options endpoint SHALL return all workcenter groups
|
||||
`GET /api/mid-section-defect/station-options` SHALL return the 12 workcenter groups from `WORKCENTER_GROUPS` as an ordered list with `name` and `order` fields.
|
||||
|
||||
#### Scenario: Station options response format
|
||||
- **WHEN** the endpoint is called
|
||||
- **THEN** it SHALL return a JSON array of 12 objects: `[{"name": "切割", "order": 0}, {"name": "焊接_DB", "order": 1}, ...]` sorted by order
|
||||
|
||||
### Requirement: All API endpoints SHALL accept station and direction parameters
|
||||
All `/api/mid-section-defect/*` endpoints (`/analysis`, `/analysis/detail`, `/loss-reasons`, `/export`) SHALL accept `station` (string, default `'測試'`) and `direction` (string, `'backward'` | `'forward'`, default `'backward'`) query parameters.
|
||||
|
||||
#### Scenario: Parameters passed to service layer
|
||||
- **WHEN** `/api/mid-section-defect/analysis?station=成型&direction=forward` is called
|
||||
- **THEN** `query_analysis()` SHALL receive `station='成型'` and `direction='forward'`
|
||||
|
||||
#### Scenario: Invalid station rejected
|
||||
- **WHEN** a station name not in `WORKCENTER_GROUPS` is provided
|
||||
- **THEN** the endpoint SHALL return HTTP 400 with an error message
|
||||
|
||||
### Requirement: Cache key SHALL include station and direction
|
||||
The cache key for analysis results SHALL include `station` and `direction` to prevent cross-contamination between different query contexts.
|
||||
|
||||
#### Scenario: Different station/direction combinations cached separately
|
||||
- **WHEN** `station=測試, direction=backward` is queried, then `station=成型, direction=forward` is queried
|
||||
- **THEN** each SHALL have its own independent cache entry
|
||||
@@ -0,0 +1,25 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: EventFetcher SHALL provide unified cached event querying across domains
|
||||
`EventFetcher` SHALL encapsulate batch event queries with L1/L2 layered cache and rate limit bucket configuration, supporting domains: `history`, `materials`, `rejects`, `holds`, `jobs`, `upstream_history`, `downstream_rejects`.
|
||||
|
||||
#### Scenario: Cache miss for event domain query
|
||||
- **WHEN** `EventFetcher` is called for a domain with container IDs and no cache exists
|
||||
- **THEN** the domain query SHALL execute against Oracle via `read_sql_df()`
|
||||
- **THEN** the result SHALL be stored in L2 Redis cache with key format `evt:{domain}:{sorted_cids_hash}`
|
||||
- **THEN** L1 memory cache SHALL also be populated (aligned with `core/cache.py` LayeredCache pattern)
|
||||
|
||||
#### Scenario: Cache hit for event domain query
|
||||
- **WHEN** `EventFetcher` is called for a domain and L2 Redis cache contains a valid entry
|
||||
- **THEN** the cached result SHALL be returned without executing Oracle query
|
||||
- **THEN** DB connection pool SHALL NOT be consumed
|
||||
|
||||
#### Scenario: Rate limit bucket per domain
|
||||
- **WHEN** `EventFetcher` is used from a route handler
|
||||
- **THEN** each domain SHALL have a configurable rate limit bucket aligned with `configured_rate_limit()` pattern
|
||||
- **THEN** rate limit configuration SHALL be overridable via environment variables
|
||||
|
||||
#### Scenario: downstream_rejects domain query
|
||||
- **WHEN** `EventFetcher` is called with domain `downstream_rejects`
|
||||
- **THEN** it SHALL load `mid_section_defect/downstream_rejects.sql` via `SQLLoader.load_with_params()` with `DESCENDANT_FILTER` set to the batched IN clause condition
|
||||
- **THEN** the query SHALL return reject records with `WORKCENTER_GROUP` classification
|
||||
@@ -0,0 +1,32 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: query-tool lineage tab SHALL load on-demand
|
||||
The query-tool lineage tree SHALL auto-fire lineage API calls after lot resolution with concurrency-limited parallel requests and progressive rendering, while preserving on-demand expand/collapse for tree navigation.
|
||||
|
||||
The mid_section_defect profile SHALL support a `direction` parameter that controls lineage resolution direction: `backward` uses `resolve_full_genealogy()` (ancestors), `forward` uses `resolve_forward_tree()` (descendants).
|
||||
|
||||
`useTraceProgress.js` `PROFILE_DOMAINS` for `mid_section_defect` SHALL include `'upstream_history'` for backward and `['upstream_history', 'downstream_rejects']` for forward. Domain selection SHALL be handled by the backend based on `direction` in params.
|
||||
|
||||
`collectAllContainerIds()` SHALL support forward direction by collecting descendants from `children_map` (instead of ancestors) when `direction='forward'` is present in params.
|
||||
|
||||
#### Scenario: Auto-fire lineage after resolve
|
||||
- **WHEN** lot resolution completes with N resolved lots
|
||||
- **THEN** lineage SHALL be fetched via `POST /api/trace/lineage` for each lot automatically
|
||||
- **THEN** concurrent requests SHALL be limited to 3 at a time to respect rate limits (10/60s)
|
||||
- **THEN** response time SHALL be ≤3s per individual lot
|
||||
|
||||
#### Scenario: Multiple lots lineage results cached
|
||||
- **WHEN** lineage data has been fetched for multiple lots
|
||||
- **THEN** each lot's lineage data SHALL be preserved independently (not re-fetched)
|
||||
- **WHEN** a new resolve query is executed
|
||||
- **THEN** all cached lineage data SHALL be cleared
|
||||
|
||||
#### Scenario: Mid-section defect backward lineage
|
||||
- **WHEN** profile is `mid_section_defect` and direction is `backward`
|
||||
- **THEN** lineage stage SHALL call `resolve_full_genealogy()` to get ancestor container IDs
|
||||
- **THEN** `collectAllContainerIds()` SHALL merge seed IDs with ancestor IDs
|
||||
|
||||
#### Scenario: Mid-section defect forward lineage
|
||||
- **WHEN** profile is `mid_section_defect` and direction is `forward`
|
||||
- **THEN** lineage stage SHALL call `resolve_forward_tree()` to get descendant container IDs
|
||||
- **THEN** `collectAllContainerIds()` SHALL merge seed IDs with descendant IDs from `children_map`
|
||||
@@ -0,0 +1,62 @@
|
||||
## 1. SQL Layer
|
||||
|
||||
- [x] 1.1 Create `station_detection.sql` — copy `tmtt_detection.sql`, replace hardcoded TMTT filter with `{{ STATION_FILTER }}` / `{{ STATION_FILTER_REJECTS }}` placeholders, rename `TMTT_EQUIPMENTID/NAME` → `DETECTION_EQUIPMENTID/NAME`
|
||||
- [x] 1.2 Create `downstream_rejects.sql` — query `DW_MES_LOTREJECTHISTORY` for batched CONTAINERIDs with `WORKCENTER_GROUP` CASE WHEN, returning CONTAINERID, WORKCENTERNAME, WORKCENTER_GROUP, LOSSREASONNAME, EQUIPMENTNAME, REJECT_TOTAL_QTY, TXNDATE
|
||||
- [x] 1.3 Modify `upstream_history.sql` — add `h.TRACKINQTY` (with COALESCE to 0) to `ranked_history` CTE and final SELECT
|
||||
|
||||
## 2. Backend Service — Station Parameterization
|
||||
|
||||
- [x] 2.1 Add `_build_station_filter(station_name, column_prefix)` to `mid_section_defect_service.py` — reads `WORKCENTER_GROUPS` patterns/exclude, builds OR-LIKE SQL with bind params
|
||||
- [x] 2.2 Replace `_fetch_tmtt_data()` with `_fetch_station_detection_data(start_date, end_date, station)` — uses `station_detection.sql` + `_build_station_filter()`
|
||||
- [x] 2.3 Update all public API signatures (`query_analysis`, `query_analysis_detail`, `export_csv`, `resolve_trace_seed_lots`, `build_trace_aggregation_from_events`) to accept `station` and `direction` params (default `'測試'`/`'backward'`)
|
||||
- [x] 2.4 Add station+direction to cache keys
|
||||
- [x] 2.5 Rename all internal `TMTT_` → `DETECTION_` references (variables, dict keys, DIMENSION_MAP entries)
|
||||
|
||||
## 3. Backend Service — Forward Pipeline
|
||||
|
||||
- [x] 3.1 Extract existing backward logic into `_run_backward_pipeline(start_date, end_date, station, loss_reasons)`
|
||||
- [x] 3.2 Add `_fetch_downstream_rejects(tracked_cids)` — batch query using `downstream_rejects.sql`
|
||||
- [x] 3.3 Implement `_attribute_forward_defects(detection_df, detection_cids, downstream_wip, downstream_rejects, station_order)` — per-station reject rate using TRACKINQTY denominator
|
||||
- [x] 3.4 Implement `_run_forward_pipeline(start_date, end_date, station, loss_reasons)` — full 8-stage pipeline (detection → forward lineage → downstream WIP+rejects → attribution → KPI/charts/detail)
|
||||
- [x] 3.5 Implement `_build_forward_kpi()`, `_build_forward_charts()`, `_build_forward_detail_table()` builders
|
||||
- [x] 3.6 Add direction dispatch in `query_analysis()`: backward → `_run_backward_pipeline()`, forward → `_run_forward_pipeline()`
|
||||
- [x] 3.7 Add `query_station_options()` — returns ordered workcenter groups list
|
||||
|
||||
## 4. Backend Routes & EventFetcher
|
||||
|
||||
- [x] 4.1 Update `mid_section_defect_routes.py` — add `station` + `direction` query params to all endpoints, add station validation, add `GET /station-options` endpoint
|
||||
- [x] 4.2 Update `trace_routes.py` — `_seed_resolve_mid_section_defect()` passes `station`; lineage stage uses direction to choose `resolve_full_genealogy()` vs `resolve_forward_tree()`; events stage passes direction for domain selection
|
||||
- [x] 4.3 Add `downstream_rejects` domain to `event_fetcher.py` — in `SUPPORTED_EVENT_DOMAINS` and `_build_domain_sql()`, loading `mid_section_defect/downstream_rejects.sql`
|
||||
|
||||
## 5. Frontend — FilterBar & App
|
||||
|
||||
- [x] 5.1 Update `FilterBar.vue` — add station `<select>` dropdown (fetches from `/station-options` on mount), add direction toggle button group (反向追溯/正向追溯), emit station+direction via `update-filters`
|
||||
- [x] 5.2 Update `App.vue` — add `station: '測試'` and `direction: 'backward'` to filters reactive, include in `buildFilterParams()`, add computed `isForward`, switch chart layout by direction, update page header to '製程不良追溯分析' with dynamic subtitle
|
||||
- [x] 5.3 Update `useTraceProgress.js` — add `downstream_rejects` to `PROFILE_DOMAINS.mid_section_defect` for forward, update `collectAllContainerIds()` to support `children_map` for forward direction
|
||||
|
||||
## 6. Frontend — Direction-Aware Components
|
||||
|
||||
- [x] 6.1 Update `KpiCards.vue` — accept `direction` + `stationLabel` props, switch card labels between backward/forward modes
|
||||
- [x] 6.2 Update `DetailTable.vue` — accept `direction` prop, switch column definitions between backward (existing) and forward (偵測設備, 偵測投入, 偵測不良, 下游到達站數, 下游不良總數, 下游不良率, 最差下游站)
|
||||
- [x] 6.3 Add `.direction-toggle` styles to `style.css`
|
||||
|
||||
## 7. Remove TMTT Defect Page
|
||||
|
||||
- [x] 7.1 Delete `frontend/src/tmtt-defect/` directory
|
||||
- [x] 7.2 Delete `src/mes_dashboard/routes/tmtt_defect_routes.py`
|
||||
- [x] 7.3 Delete `src/mes_dashboard/services/tmtt_defect_service.py`
|
||||
- [x] 7.4 Delete `src/mes_dashboard/sql/tmtt_defect/` directory
|
||||
- [x] 7.5 Remove tmtt-defect registration from `nativeModuleRegistry.js`, `routeContracts.js`, `vite.config.js`, `page_status.json`, `routes/__init__.py`, `app.py`, `page_registry.py`, and all migration baseline/config files
|
||||
- [x] 7.6 Delete related test files and update remaining tests referencing tmtt-defect
|
||||
|
||||
## 8. Config & Metadata
|
||||
|
||||
- [x] 8.1 Update `page_status.json` — rename mid-section-defect page name from '中段製程不良追溯' to '製程不良追溯分析', remove tmtt-defect entry
|
||||
|
||||
## 9. Verification
|
||||
|
||||
- [x] 9.1 Run `python -m pytest tests/test_mid_section_defect_*.py -v` — all 22 tests pass
|
||||
- [x] 9.2 Run `cd frontend && node --test` — 69/69 frontend tests pass
|
||||
- [x] 9.3 Run all change-relevant backend tests (app_factory, navigation_contract, full_modernization_gates, page_registry, portal_shell_wave_b_native_smoke) — 64/64 pass
|
||||
- [x] 9.4 Verify backward compat: `station=測試, direction=backward` produces identical data (renamed columns) — 25,415 detail rows, DETECTION_EQUIPMENTNAME columns (no TMTT_), KPI/charts/genealogy all correct
|
||||
- [x] 9.5 Verify forward basic: `station=成型 (order=4), direction=forward` → 8 downstream stations, 1,673 detail rows, downstream reject distribution: 測試 1.67%, 水吹砂 0.03%, 切彎腳 0.03%, 去膠 0.02%, 電鍍 0.01%, 移印 0.01%
|
||||
Reference in New Issue
Block a user