S-ORD 委託回報查詢 Feature Spec
Feature 前綴:S-ORD | Feature 目錄:
orderReports| Route:/order-reports| Backend Controller:OrderReportsController| 資料來源:OrderDetail(委託明細)+MatchDetail(成交回報)本頁摘要 Feature Spec;完整內容見 spec.md。feature-map 對應列:FSTS 功能對應表 §完整對應表。
需求摘要
「委託回報查詢」是唯讀查詢功能模組,供交易員查閱委託送出後的執行回報紀錄(含委託狀態、成交結果)。1
業務背景:複委託流程中,交易員透過 COBOL 主機系統送出委託(Order)後,外部清算系統(如 CBL)回傳成交回報(Match/Trade Confirmation);資料透過 FD 檔案轉入 OrderDetail 與 MatchDetail 資料表。此模組提供統一查詢介面,交易員無需直接存取資料庫。1
使用者故事:交易員希望查詢系統中所有客戶的委託回報紀錄,以追蹤委託執行狀態、核對成交結果、並在客戶詢問時快速定位委託資訊。1
Scope
In Scope
2 本 Feature 涵蓋:
- 委託回報列表查詢(讀取
OrderDetail) - 多維篩選:客戶帳號、股票代號、委託狀態(
ProcessFlag)、買賣別(BSCode)、委託日期區間(OrderDate)、券商代號、上手券商代號 - 列表分頁(預設 20,上限 500)
- 單筆明細查詢(JOIN
MatchDetail取得成交回報清單) - 前端列表頁
/order-reports篩選列、分頁表格、查看詳情按鈕 - 權限
OrderReports.View(Admin / User 角色預設皆擁有) - 文件同步:
feature-map.md、scenarios.md、domain.md、db-schema.md
Out of Scope
2 留給後續:
- 委託建立/修改/刪除、下單、撤單(本功能為純查詢)
- 成交回報手動輸入或修改
- 匯出(CSV/Excel)、即時推播(WebSocket/SignalR)
- 跨客戶成本分析或盈虧計算
MatchDetail獨立列表頁(作為委託明細的子資料呈現)
資料模型
3 兩張表均由 COBOL FD 轉檔寫入(FSIO FD / FHIO-F FD),本功能不寫入:
OrderDetail(委託明細,唯讀)
複合主鍵:OrderDate + BrokerNo + OrderNo
主要欄位:OrderDate(char8 YYYYMMDD)、BrokerNo(券商代號)、OrderNo(委託書號 TERM+DSEQ 如 X0001)、AccountNo、StockSymbol、ExchangeCode、Currency、OrderShare / CancelShare / MatchShare、OrderPrice、BSCode(B/S)、OrderType(0 現股 / 1 融資 / 2 融券)、TradingType(1 價格限制 / 2 時間限制 / 3 交易指示)、ReservedAmount(圈存金額)、OrderFlag(1 下單 / 2 刪單)、ProcessFlag(空白/0/1/S/E/P/R)、RejectReason、OrderSource(0 一般 / 1 網路)、UpstreamBrokerNo、ExchangeRate。
MatchDetail(成交回報,唯讀)
複合主鍵:MatchDate + OrderNo + ExecAllocSeqNo
關聯:MatchDetail.OrderNo = OrderDetail.OrderNo AND MatchDetail.BrokerNo = OrderDetail.BrokerNo;一筆委託可對多筆成交(部分成交)。
欄位分 3 組:
- 基本資訊:MatchDate、ExecAllocSeqNo(分單號)、ExecutingBrokerNo、MatchTime(HHMMSS00)、MatchAmount、NetAmount、ReservedAmount、MarginSettlementDate、EquitySettlementDate、ExchangeRate。
- 客戶費用(FEE OCCURS 1~8):手續費、處理費、交易所費、結算費、匯款手續費、交易稅、印花稅、PTP 交易稅。
- 上手費用(UPFEE OCCURS 1~12):上手版本的手續費/處理費/交易所費/結算費/匯款手續費/交易稅/印花稅/股票服務費/營業稅/保管費/營業成本/交割費用。
COBOL → SQL 欄位對應(TMMDD→OrderDate、SHEET→OrderNo、CSEQ→AccountNo 等)於 spec §Data Model 完整列出。
API 設計
4 兩條端點:
| 方法 | 路徑 | 說明 |
|---|---|---|
| GET | /api/v1/order-reports | 分頁查詢列表 |
| GET | /api/v1/order-reports/{orderDate}/{brokerNo}/{orderNo} | 單筆明細(含成交清單) |
GET /api/v1/order-reports
4 Query 參數:page、pageSize、brokerNo、accountNo、stockSymbol、processFlag、bsCode、orderDateFrom、orderDateTo、upstreamBrokerNo。
Response:PagedResponse<OrderReportResponse>,含 page / pageSize / totalCount / totalPages / items[],每筆帶中英欄位 label(如 bsCodeLabel、processFlagLabel、tradingTypeLabel)、計算欄位 remainShare 與 matchCount。
計算欄位:remainShare = OrderShare - CancelShare - MatchShare(非 DB 欄位,DTO 層以 Math.Max(0, ...) 保護,負數截為 0)。4
FluentValidation:pageSize 介於 1~500;orderDateFrom/To 須合法 YYYY-MM-DD;orderDateFrom <= orderDateTo;bsCode 僅允許 B 或 S;processFlag 僅允許(空白/0/1/S/E/P/R)。4
GET /api/v1/order-reports/{orderDate}/{brokerNo}/{orderNo}
5 Path 參數:orderDate(YYYYMMDD)、brokerNo、orderNo。
Response:OrderReportDetailResponse,含完整委託欄位 + matchDetails[](無成交為空陣列,不可為 null);每筆成交含基本資訊 + 客戶費用(FEE 18)+ 上手費用(UPFEE 112)。
User Scenarios
主流程 S-ORD-001 ~ 010
6 全為 Full Suite(情境編號 001 起始;S-ORD 為新模組):
- 001 預設查詢:進入
/order-reports,依OrderDate降序、每頁 20 筆 - 002 依券商代號篩選(如 9699)
- 003 依客戶帳號篩選(如 100001)
- 004 依股票代號篩選(如 AAPL,不分大小寫)
- 005 依委託狀態篩選(如「已完成」→
ProcessFlag='S') - 006 依買賣別篩選(買進 →
BSCode='B') - 007 依委託日期區間(如 2026-01-01 ~ 2026-03-31)
- 008 組合篩選:券商 + 買賣別 + 日期區間
- 009 展開單筆成交明細(
matchCount > 0) - 010 查看無成交紀錄的委託(
matchCount = 0→ 顯示「尚無成交紀錄」)
異常流程
| 情境 | 觸發條件 | 預期結果 |
|---|---|---|
S-ORD-ERR-001 | 無符合紀錄 | 200 + items: [] + Empty State |
S-ORD-ERR-002 | pageSize=501 | 400 ProblemDetails「分頁大小不得超過 500」 |
S-ORD-ERR-003 | 非法日期字串(如 2026-13-01) | 400 ProblemDetails 日期格式錯誤 |
S-ORD-ERR-004 | 單筆查詢 PK 不存在 | 404 ProblemDetails「委託回報不存在」 |
S-ORD-ERR-005 | 查詢 API 逾時 | 前端 Error Toast「查詢逾時,請稍後再試」 |
S-ORD-ERR-006 | 無 OrderReports.View 權限 | 前端 PermissionGuard;API 回 403 |
UI 驗證情境
- S-ORD-VAL-001 起始日期晚於結束日期 → inline 錯誤「起始日期不得晚於結束日期」,查詢鍵停用
- S-ORD-VAL-002
bsCode非 B/S → 後端 FluentValidation 回 400
Acceptance Criteria
後端 API(AC-01 ~ 13)
- AC-01 無篩選回 200,
items含基本欄位 - AC-02 ~ 06 各單一篩選正確生效,包含
stockSymbol=aapl須比對到AAPL - AC-07
pageSize=501→ 400 RFC 7807 - AC-08 ~ 09 單筆查詢存在回 200 含
matchDetails;不存在回 404 - AC-10 無權限 JWT → 403
- AC-11 回應為
PagedResponse<OrderReportResponse>(page/pageSize/totalCount/totalPages) - AC-12
remainShare = max(0, OrderShare - CancelShare - MatchShare),不得為負 - AC-13
matchDetails為陣列,無成交時為[](不可為 null)
前端(AC-14 ~ 22)
10 篩選列 + 表格;套用篩選分頁重置;查無資料顯 Empty State;「查看詳情」按鈕導向 /order-reports/{orderDate}/{brokerNo}/{orderNo};詳情頁顯示委託基本資訊 Card + 成交明細 Card;無成交顯「尚無成交紀錄」;orderDateFrom > orderDateTo inline 錯誤不送 API;ProcessFlag Badge 配色(S 綠 / E R 紅 / 1 藍 / 0 空白灰 / P 黃);BSCode Badge(B 藍買進 / S 橘賣出);詳情頁「← 返回列表」按鈕導回列表。
UI 設計
11 UI 設計狀態:✅ 已完成,設計稿 pencil-new.pen(共用畫布),四個 Frame:列表頁、Empty State、篩選驗證錯誤、詳情頁。
列表頁:篩選列欄位(券商 Input、客戶帳號 Number、股票代號、委託狀態 Dropdown 多選、買賣別 Dropdown 單選、委託日期起迄 DatePicker、上手券商 Input)+ 資料表格(含展開/查看詳情按鈕)+ 分頁列。12
詳情頁:委託基本資訊 Card(5 欄 Grid,右上角顯示 ProcessFlag Badge)+ 成交明細 Card(成交清單表格 + 每筆成交的「分單號 N — 費用明細」區塊,客戶費用與上手費用分兩子區;上手費用以 4 欄 Grid 排列 UPFEE 12 欄)。13
ProcessFlag Badge 對應:空白/0=灰(未處理)、1=藍(已傳出)、S=綠(已完成)、E=紅(錯誤)、P=黃(人工)、R=紅(拒絕)。12
Edge Cases
14 重點處理:
OrderShare - CancelShare - MatchShare負值 →remainShare強制為 0(DTO 層Math.Max(0, …))ProcessFlag空白等同 ‘0’(未處理),查詢時兩者視為同義(WHERE ProcessFlag IN ('', '0'))OrderDate儲存為char(8) YYYYMMDD;篩選傳入YYYY-MM-DD須在 Repository 層轉換比對MatchTime為char(8) HHMMSS00,前端截前 6 碼顯示為 HH:MM:SS- 孤兒
MatchDetail(無對應OrderDetail)不會出現在列表(以 OrderDetail 為主表) - 分頁超過最後一頁回 200 + 空陣列
stockSymbol不分大小寫比對(UPPER()或EF.Functions.Like)- 日期區間跨度大(如 10 年)→ 依賴
OrderDate索引(Risks 追蹤)
Tasks
Backend 層(T-01 ~ 12)
15 主要檔案:
- Application:
GetOrderReportsQuery/GetOrderReportDetailQuery(含 FluentValidation;Detail Handler 查不到時拋NotFoundException→ 404) - DTO:
OrderReportResponse(含remainShare計算)、OrderReportDetailResponse(含matchDetails)、MatchDetailResponse(基本資訊 + FEE 18 + UPFEE 112 全欄位) - Repository:
IOrderReportRepository/OrderReportRepository(GetPagedAsync動態條件 +stockSymbol不分大小寫 + 日期格式轉換;GetDetailAsyncJOIN +matchCount) - Controller:
OrderReportsController兩條路由掛[RequirePermission("OrderReports", "View")] - 資源定義:
ResourceDefinitions.OrderReports.View - SQL:
015_add_order_reports_permissions.sql(Admin / User 角色 RolePermission seed) - Unit Test:
GetOrderReportsQueryHandler(8 情境 +remainShare負值保護)、GetOrderReportDetailQueryHandler(有/無成交 / 404)
Frontend 層(T-13 ~ 26)
16 主要檔案(src/features/orderReports/):
- Types:
orderReportsTypes.ts(OrderReport,OrderReportDetail,MatchDetailItem,OrderReportsQueryParams,ProcessFlagValue,BSCodeValue) - API:
src/services/api/orderReportsApi.ts+api/orderReportsQueryKeys.ts - Hook:
useOrderReportsQuery(列表)、useOrderReportDetailQuery(lazy,展開時才觸發) - UI:
OrderReportsFilterBar.tsx、OrderReportsTable.tsx、ProcessFlagBadge.tsx、BSCodeBadge.tsx、ListPage.tsx、DetailPage.tsx - 路由:
src/app/(dashboard)/order-reports/page.tsx與[orderDate]/[brokerNo]/[orderNo]/page.tsx(均PermissionGuard包裝) - Sidebar:
src/shared/constants/appConstants.ts新增/order-reportsentry
文件同步(T-26 ~ 29)
17 需更新:docs/domain/feature-map.md(新增 S-ORD 列)、docs/domain/scenarios.md(S-ORD 情境編號與 Robot Framework Keyword 欄位)、docs/domain/domain.md(術語「委託回報 Order Report」、「成交回報 Match Detail」;BR-011 商業規則;OrderReports.View 權限列)、docs/domain/db-schema.md(OrderDetail / MatchDetail 標注 S-ORD 使用)。
Risks
| 風險 | 等級 | 緩解 |
|---|---|---|
OrderDetail 資料量龐大,全表掃描慢 | 🔴 高 | 確認 OrderDate/BrokerNo/StockSymbol 索引;必要時以 DB Script 補(不得改 FD 轉入資料) |
OrderDate(char8 YYYYMMDD)轉換邏輯邊界錯誤 | 🟠 中 | Repository 單元測試覆蓋跨月/年底 |
MatchDetail JOIN 與 matchCount 效能 | 🟠 中 | 列表只查 matchCount(COUNT 子查詢),展開時才 lazy load;確認 OrderNo + BrokerNo 索引 |
remainShare 負值(資料異常) | 🟡 低 | DTO 層 Math.Max(0, …) |
ProcessFlag 空白 vs ‘0’ 語意 | 🟡 低 | 查詢條件 WHERE ProcessFlag IN ('', '0') |
| 依賴 COBOL FD 轉檔正確性 | 🟠 中 | 本功能僅查詢;Empty State 提示「資料來源為轉檔結果」 |
| EF Core Migration 歷史衝突(SQL Script 序號) | 🟡 低 | 開發前確認最新序號;此次為 015 |
相關頁面
- 功能對應:FSTS 功能對應表(S-ORD 列)
- OMS 子系統:OMS 總覽
- 核心流程:FSTS 核心業務流程(FD 轉檔)
- DB Schema:FSTS DB Schema
補充資訊
(未來 ingest 新來源會在此追加段落)